home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / othergnu / texinf~1.zoo / texinfo.st / makeinfo / makeinfo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-23  |  210.0 KB  |  8,720 lines

  1. /* Makeinfo -- convert texinfo format files into info files.
  2.  
  3.    Copyright (C) 1987, 1992, 1993 Free Software Foundation, Inc.
  4.  
  5.    This file is part of GNU Info.
  6.  
  7.    Makeinfo is distributed in the hope that it will be useful,
  8.    but WITHOUT ANY WARRANTY.  No author or distributor accepts
  9.    responsibility to anyone for the consequences of using it or for
  10.    whether it serves any particular purpose or works at all, unless he
  11.    says so in writing.  Refer to the GNU Emacs General Public License
  12.    for full details.
  13.  
  14.    Everyone is granted permission to copy, modify and redistribute
  15.    Makeinfo, but only under the conditions described in the GNU Emacs
  16.    General Public License.   A copy of this license is supposed to
  17.    have been given to you along with GNU Emacs so you can know your
  18.    rights and responsibilities.  It should be in a file named COPYING.
  19.    Among other things, the copyright notice and this notice must be
  20.    preserved on all copies.  */
  21.  
  22. /* This is Makeinfo version 1.55.  If you change the version number of
  23.    Makeinfo, please change it here and at the lines reading:
  24.  
  25.     int major_version = 1;
  26.     int minor_version = 55;
  27.  
  28.    in the code below.
  29.  
  30.    Makeinfo is authored by Brian Fox (bfox@ai.mit.edu). */
  31.  
  32. /* You can change some of the behaviour of Makeinfo by changing the
  33.    following defines: */
  34.  
  35. /* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
  36.    appear within an @table, @ftable, or @itemize environment to have
  37.    standard paragraph indentation.  Without this, such paragraphs have
  38.    no starting indentation. */
  39. /* #define INDENT_PARAGRAPHS_IN_TABLE */
  40.  
  41. /* Define DEFAULT_INDENTATION_INCREMENT as an integer which is the amount
  42.    that @example should increase indentation by.  This increment is used
  43.    for all insertions which indent the enclosed text. */
  44. #define DEFAULT_INDENTATION_INCREMENT 5
  45.  
  46. /* Define PARAGRAPH_START_INDENT to be the amount of indentation that
  47.    the first lines of paragraphs receive by default, where no other
  48.    value has been specified.  Users can change this value on the command
  49.    line, with the +paragraph-indent option, or within the texinfo file,
  50.    with the @paragraphindent command. */
  51. #define PARAGRAPH_START_INDENT 3
  52.  
  53. /* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
  54.    wish to appear between paragraphs.  A value of 1 creates a single blank
  55.    line between paragraphs.  Paragraphs are defined by 2 or more consecutive
  56.    newlines in the input file (i.e., one or more blank lines). */
  57. #define DEFAULT_PARAGRAPH_SPACING 1
  58.  
  59. /* **************************************************************** */
  60. /*                                    */
  61. /*            Include File Declarations               */
  62. /*                                    */
  63. /* **************************************************************** */
  64.  
  65. /* Indent #pragma so that older Cpp's don't try to parse it. */
  66. #if defined (_AIX)
  67.  # pragma alloca
  68. #endif /* _AIX */
  69.  
  70. #ifndef __STDC__
  71. #define __STDC__ 0
  72. #endif
  73.  
  74. #include <stdio.h>
  75. #if __STDC__
  76. #include <stdlib.h>
  77. #include <stdarg.h>
  78. #if defined (HAVE_VARARGS_H)
  79. #undef HAVE_VARARGS_H
  80. #endif /* HAVE_VARARGS_H */
  81. #endif /* __STDC__ */
  82. #include <sys/types.h>
  83. #include <ctype.h>
  84. #include <sys/stat.h>
  85. #include <ctype.h>
  86. #include <pwd.h>
  87. #include <errno.h>
  88. #if defined (HAVE_VARARGS_H)
  89. #include <varargs.h>
  90. #endif /* HAVE_VARARGS_H */
  91. #include "getopt.h"
  92.  
  93. #if defined (VMS)
  94. #include <perror.h>
  95. #endif
  96.  
  97. #if defined (HAVE_STRING_H)
  98. #include <string.h>
  99. #else
  100. #include <strings.h>
  101. #endif /* !HAVE_STRING_H */
  102.  
  103. #if defined (TM_IN_SYS_TIME)
  104. #include <sys/time.h>
  105. #else
  106. #include <time.h>
  107. #endif /* !TM_IN_SYS_TIME */
  108.  
  109. #if defined (HAVE_SYS_FCNTL_H)
  110. #include <sys/fcntl.h>
  111. #else
  112. #include <fcntl.h>
  113. #endif /* !HAVE_SYS_FCNTL_H */
  114.  
  115. #if defined (HAVE_SGTTY_H)
  116. #include <sgtty.h>
  117. #else
  118. #include <termio.h> /* ??? */
  119. #endif /* HAVE_SGTTY_H */
  120.  
  121. #include <sys/file.h>
  122.  
  123. #if defined (__GNUC__)
  124. #ifndef alloca
  125. #define alloca __builtin_alloca
  126. #endif /* alloca */
  127. #else
  128. #if defined(HAVE_ALLOCA_H)
  129. #include <alloca.h>
  130. #else /* !HAVE_ALLOCA_H */
  131. #if !defined (_AIX)
  132. #ifndef alloca
  133. extern char *alloca ();
  134. #endif
  135. #endif /* !_AIX */
  136. #endif /* !HAVE_ALLOCA_H */
  137. #endif /* !__GNUC__ */
  138.  
  139. #ifdef NeXT
  140. extern int open(const char *, int, ...), close(int);
  141. extern int read(int, void *, int), write(int, const void *, int);
  142. extern int ioctl(int, unsigned long, void *);
  143. extern int tgetent(char *, char *);
  144. extern int tgetnum(char *);
  145. #endif /* NeXT */
  146.  
  147. #ifdef atarist
  148. /* #include <unistd.h> */ /* don't - clash with getopt.h from GNU */
  149. extern int open (const char *, int, ...), close(int);
  150. extern long _read (int, void *, unsigned long);
  151. #include <support.h>
  152. #include <termcap.h>
  153. /* #define DOTS    */    /* define this if you would like to have 
  154.                a visual indicator that computer still works */
  155. #define STDERR stdout    /* hack around redirection problems */
  156. #define WMODE  "w"    /* allow for easy modifications     */
  157.             /* we assume that library is smart enough */
  158.             /* and write() behaves appriopriately     */
  159. #else  /* not-atarist */
  160. #define STDERR stderr
  161. #define WMODE  "w"
  162. #endif /* atarist */
  163.  
  164. #ifndef __PROTO
  165. #if (__STDC__) || defined(__cplusplus)
  166. # define __PROTO(s) s
  167. #else
  168. # define __PROTO(s) ()
  169. #endif
  170. #endif
  171.  
  172. /* void *xmalloc (), *xrealloc (); */
  173. static void isolate_nodename __PROTO((char *nodename));
  174.  
  175. /* Non-zero means that we are currently hacking the insides of an
  176.    insertion which would use a fixed width font. */
  177. static int in_fixed_width_font = 0;
  178.  
  179. /* Non-zero means that start_paragraph () MUST be called before we pay
  180.    any attention to close_paragraph () calls. */
  181. int must_start_paragraph = 0;
  182.  
  183. /* Some systems don't declare this function in pwd.h. */
  184. extern struct passwd *getpwnam __PROTO((const char *));
  185.  
  186.  
  187. /* **************************************************************** */
  188. /*                                    */
  189. /*                  Global Defines                  */
  190. /*                                    */
  191. /* **************************************************************** */
  192.  
  193. /* Error levels */
  194. #define NO_ERROR 0
  195. #define SYNTAX     2
  196. #define FATAL     4
  197.  
  198. /* How to allocate permanent storage for STRING. */
  199. #define savestring(x) \
  200.   ((char *)strcpy ((char *)xmalloc (1 + ((x) ? strlen (x) : 0)), \
  201.            (x) ? (x) : ""))
  202.  
  203. /* C's standard macros don't check to make sure that the characters being
  204.    changed are within range.  So I have to check explicitly. */
  205.  
  206. /* GNU Library doesn't have toupper().  Until GNU gets this fixed, I will
  207.    have to do it. */
  208. #ifndef atarist
  209. /* the above does not apply to gnulib on AtariST which is not GNU Library */
  210. #ifndef toupper
  211. #define toupper(c) ((c) - 32)
  212. #endif
  213.  
  214. #define coerce_to_upper(c) ((islower(c) ? toupper(c) : (c)))
  215. #define coerce_to_lower(c) ((isupper(c) ? tolower(c) : (c)))
  216. #else  /* on ST toupper and tolower include checks - use _to...  instead */
  217. #define coerce_to_upper(c) ((islower(c) ? _toupper(c) : (c)))
  218. #define coerce_to_lower(c) ((isupper(c) ? _tolower(c) : (c)))
  219. #endif /* atarist */
  220.  
  221. #ifdef META
  222. #undef META
  223. #endif
  224.  
  225. #define control_character_bit 0x40 /* %01000000, must be off. */
  226. #define meta_character_bit 0x080/* %10000000, must be on.  */
  227. #define CTL(c) ((c) & (~control_character_bit))
  228. #define UNCTL(c) coerce_to_upper(((c)|control_character_bit))
  229. #define META(c) ((c) | (meta_character_bit))
  230. #define UNMETA(c) ((c) & (~meta_character_bit))
  231.  
  232. #define whitespace(c) (((c) == '\t') || ((c) == ' '))
  233. #define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!')
  234. #ifdef atarist
  235. #define cr_or_whitespace(c) isspace((c))  /* small optimization */
  236. #else
  237. #define cr_or_whitespace(c) (((c) == '\t') || ((c) == ' ') || ((c) == '\n'))
  238. #endif /* atarist */
  239.  
  240. #ifndef isletter
  241. #define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
  242. #endif
  243.  
  244. #ifndef isupper
  245. #define isupper(c) ((c) >= 'A' && (c) <= 'Z')
  246. #endif
  247.  
  248. #ifndef isdigit
  249. #define isdigit(c)  ((c) >= '0' && (c) <= '9')
  250. #endif
  251.  
  252. #ifndef digit_value
  253. #define digit_value(c) ((c) - '0')
  254. #endif
  255.  
  256. #define member(c, s) (strchr (s, c) != NULL)
  257.  
  258. #define COMMAND_PREFIX '@'
  259.  
  260. /* Stuff for splitting large files. */
  261. #define SPLIT_SIZE_THRESHOLD 70000  /* What's good enough for Stallman... */
  262. #define DEFAULT_SPLIT_SIZE 50000    /* Is probably good enough for me. */
  263. int splitting = 1;            /* Always true for now. */
  264.  
  265. typedef void /* int */ FUNCTION ();    /* So I can say FUNCTION *foo; */
  266.  
  267.  
  268. /* **************************************************************** */
  269. /*                                    */
  270. /*                Global Variables                */
  271. /*                                    */
  272. /* **************************************************************** */
  273.  
  274. /* Global pointer to argv[0]. */
  275. char *progname;
  276.  
  277. /* The current input file state. */
  278. char *input_filename;
  279. char *input_text;
  280. size_t size_of_input_text;
  281. size_t input_text_offset;
  282. size_t line_number;
  283.  
  284. #define curchar() input_text[input_text_offset]
  285.  
  286. #if __STDC__
  287. #define command_char(c) ((!isspace(c)) && \
  288.              ((c) != '{') && \
  289.              ((c) != '}') && \
  290.              ((c) != '='))
  291. #else  /*!__STDC__ */
  292. #define command_char(c) ((!whitespace(c)) && \
  293.              ((c) != '\n') && \
  294.              ((c) != '{') && \
  295.              ((c) != '}') && \
  296.              ((c) != '='))
  297. #endif /* __STDC__ */
  298.  
  299. #define skip_whitespace() while (input_text_offset != size_of_input_text \
  300.                  && whitespace(curchar()))\
  301.   input_text_offset++
  302.  
  303. /* Return non-zero if STRING is the text at input_text + input_text_offset,
  304.    else zero. */
  305. #define looking_at(string) \
  306.   (strncmp (input_text + input_text_offset, string, strlen (string)) == 0)
  307.  
  308. /* And writing to the output. */
  309.  
  310. /* The output file name. */
  311. char *output_filename = (char *)NULL;
  312. char *pretty_output_filename;
  313.  
  314. /* Name of the output file that the user elected to pass on the command line.
  315.    Such a name overrides any name found with the @setfilename command. */
  316. char *command_output_filename = (char *)NULL;
  317.  
  318. /* A colon separated list of directories to search for files included
  319.    with @include.  This can be controlled with the `-I' option to makeinfo. */
  320. char *include_files_path = (char *)NULL;
  321.  
  322. /* Current output stream. */
  323. FILE *output_stream;
  324.  
  325. /* Position in the output file. */
  326. size_t output_position;
  327.  
  328. /* Output paragraph buffer. */
  329. unsigned char *output_paragraph;
  330.  
  331. /* Offset into OUTPUT_PARAGRAPH. */
  332. size_t output_paragraph_offset;
  333.  
  334. /* The output paragraph "cursor" horizontal position. */
  335. int output_column = 0;
  336.  
  337. /* Non-zero means output_paragraph contains text. */
  338. int paragraph_is_open = 0;
  339.  
  340. #define INITIAL_PARAGRAPH_SPACE 5000
  341. size_t paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
  342.  
  343. /* Filling.. */
  344. /* Non-zero indicates that filling will take place on long lines. */
  345. int filling_enabled = 1;
  346.  
  347. /* Non-zero means that words are not to be split, even in long lines.  This
  348.    gets changed for cm_w (). */
  349. int non_splitting_words = 0;
  350.  
  351. /* Non-zero indicates that filling a line also indents the new line. */
  352. int indented_fill = 0;
  353.  
  354. /* The column at which long lines are broken. */
  355. int fill_column = 72;
  356.  
  357. /* The amount of indentation to apply at the start of each line. */
  358. int current_indent = 0;
  359.  
  360. /* The amount of indentation to add at the starts of paragraphs.
  361.    0 means don't change existing indentation at paragraph starts.
  362.    > 0 is amount to indent new paragraphs by.
  363.    < 0 means indent to column zero by removing indentation if necessary.
  364.  
  365.    This is normally zero, but some people prefer paragraph starts to be
  366.    somewhat more indented than paragraph bodies.  A pretty value for
  367.    this is 3. */
  368. int paragraph_start_indent = PARAGRAPH_START_INDENT;
  369.  
  370. /* Non-zero means that the use of paragraph_start_indent is inhibited.
  371.    @example uses this to line up the left columns of the example text.
  372.    A negative value for this variable is incremented each time it is used.
  373.    @noindent uses this to inhibit indentation for a single paragraph.  */
  374. int inhibit_paragraph_indentation = 0;
  375.  
  376. /* Indentation that is pending insertion.  We have this for hacking lines
  377.    which look blank, but contain whitespace.  We want to treat those as
  378.    blank lines. */
  379. int pending_indent = 0;
  380.  
  381. /* The amount that indentation increases/decreases by. */
  382. int default_indentation_increment = DEFAULT_INDENTATION_INCREMENT;
  383.  
  384. /* Non-zero indicates that indentation is temporarily turned off. */
  385. int no_indent = 1;
  386.  
  387. /* Non-zero means forcing output text to be flushright. */
  388. int force_flush_right = 0;
  389.  
  390. /* Non-zero means that the footnote style for this document was set on
  391.    the command line, which overrides any other settings. */
  392. int footnote_style_preset = 0;
  393.  
  394. /* Non-zero means that we automatically number footnotes that have no
  395.    specified marker. */
  396. int number_footnotes = 1;
  397.  
  398. /* The current footnote number in this node.  Each time a new node is
  399.    started this is reset to 1. */
  400. int current_footnote_number = 1;
  401.  
  402. /* Command name in the process of being hacked. */
  403. char *command;
  404.  
  405. /* The index in our internal command table of the currently
  406.    executing command. */
  407. int command_index;
  408.  
  409. /* A stack of file information records.  If a new file is read in with
  410.    "@input", we remember the old input file state on this stack. */
  411. typedef struct fstack
  412. {
  413.   struct fstack *next;
  414.   char *filename;
  415.   char *text;
  416.   size_t size;
  417.   size_t offset;
  418.   size_t line_number;
  419. } FSTACK;
  420.  
  421. FSTACK *filestack = (FSTACK *) NULL;
  422.  
  423. /* Stuff for nodes. */
  424. /* The current nodes node name. */
  425. char *current_node = (char *)NULL;
  426.  
  427. /* The current nodes section level. */
  428. int current_section = 0;
  429.  
  430. /* The filename of the current input file.  This is never freed. */
  431. char *node_filename = (char *)NULL;
  432.  
  433. /* What we remember for each node. */
  434. typedef struct tentry
  435. {
  436.   struct tentry *next_ent;
  437.   char *node;        /* name of this node. */
  438.   char *prev;        /* name of "Prev:" for this node. */
  439.   char *next;        /* name of "Next:" for this node. */
  440.   char *up;        /* name of "Up:" for this node.   */
  441.   size_t position;    /* output file position of this node. */
  442.   size_t line_no;    /* defining line in source file. */
  443.   char *filename;    /* The file that this node was found in. */
  444.   int touched;        /* non-zero means this node has been referenced. */
  445.   int flags;        /* Room for growth.  Right now, contains 1 bit. */
  446. } TAG_ENTRY;
  447.  
  448. /* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a,
  449.    we turn on this flag bit in node-b's tag entry.  This means that when
  450.    it is time to validate node-b, we don't report an additional error
  451.    if there was no "Prev" field. */
  452. #define PREV_ERROR 0x1
  453. #define NEXT_ERROR 0x2
  454. #define UP_ERROR   0x4
  455. #define NO_WARN       0x8
  456. #define IS_TOP        0x10
  457.  
  458. TAG_ENTRY *tag_table = (TAG_ENTRY *) NULL;
  459.  
  460. #define HAVE_MACROS
  461. #if defined (HAVE_MACROS)
  462. /* Macro definitions for user-defined commands. */
  463. typedef struct {
  464.   char *name;            /* Name of the macro. */
  465.   char *definition;        /* Definition text. */
  466.   char *filename;        /* File where this macro is defined. */
  467.   size_t lineno;        /* Line number within FILENAME. */
  468. } MACRO_DEF;
  469.  
  470. /**
  471. void add_macro (), execute_macro ();
  472. MACRO_DEF *find_macro (), *delete_macro ();
  473. **/
  474. #endif /* HAVE_MACROS */
  475.  
  476. /* Menu reference, *note reference, and validation hacking. */
  477.  
  478. /* The various references that we know about. */
  479. enum reftype
  480. {
  481.   menu_reference, followed_reference
  482. };
  483.  
  484. /* A structure to remember references with.  A reference to a node is
  485.    either an entry in a menu, or a cross-reference made with [px]ref. */
  486. typedef struct node_ref
  487. {
  488.   struct node_ref *next;
  489.   char *node;            /* Name of node referred to. */
  490.   char *containing_node;    /* Name of node containing this reference. */
  491.   size_t line_no;        /* Line number where the reference occurs. */
  492.   int section;            /* Section level where the reference occurs. */
  493.   char *filename;        /* Name of file where the reference occurs. */
  494.   enum reftype type;        /* Type of reference, either menu or note. */
  495. } NODE_REF;
  496.  
  497. /* The linked list of such structures. */
  498. NODE_REF *node_references = (NODE_REF *) NULL;
  499.  
  500. /* Flag which tells us whether to examine menu lines or not. */
  501. int in_menu = 0;
  502.  
  503. /* Flags controlling the operation of the program. */
  504.  
  505. /* Default is to notify users of bad choices. */
  506. int print_warnings = 1;
  507.  
  508. /* Default is to check node references. */
  509. int validating = 1;
  510.  
  511. /* Non-zero means do not output "Node: Foo" for node separations. */
  512. int no_headers = 0;
  513.  
  514. /* Number of errors that we tolerate on a given fileset. */
  515. int max_error_level = 100;
  516.  
  517. /* Maximum number of references to a single node before complaining. */
  518. int reference_warning_limit = 1000;
  519.  
  520. /* Non-zero means print out information about what is going on when it
  521.    is going on. */
  522. int verbose_mode = 0;
  523.  
  524. /* Non-zero means run only splitting part on already made info file */
  525. int only_split = 0;
  526.  
  527. /* The list of commands that we hack in texinfo.  Each one
  528.    has an associated function.  When the command is encountered in the
  529.    text, the associated function is called with START as the argument.
  530.    If the function expects arguments in braces, it remembers itself on
  531.    the stack.  When the corresponding close brace is encountered, the
  532.    function is called with END as the argument. */
  533.  
  534. #define START 0
  535. #define END 1
  536.  
  537. typedef struct brace_element
  538. {
  539.   struct brace_element *next;
  540.   FUNCTION *proc;
  541.   size_t pos, line;
  542. } BRACE_ELEMENT;
  543.  
  544. BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL;
  545.  
  546. /* Forward declarations. */
  547.  
  548. #ifdef NOT_USED
  549. int insert_self (), cm_ignore_line ();
  550.  
  551. int
  552.   cm_tex (), cm_asterisk (), cm_dots (), cm_bullet (), cm_TeX (),
  553.   cm_copyright (), cm_code (), cm_samp (), cm_file (), cm_kbd (),
  554.   cm_key (), cm_ctrl (), cm_var (), cm_dfn (), cm_emph (), cm_strong (),
  555.   cm_cite (), cm_italic (), cm_bold (), cm_roman (), cm_title (), cm_w (),
  556.   cm_refill (), cm_titlefont ();
  557.  
  558. int
  559.   cm_chapter (), cm_unnumbered (), cm_appendix (), cm_top (),
  560.   cm_section (), cm_unnumberedsec (), cm_appendixsec (),
  561.   cm_subsection (), cm_unnumberedsubsec (), cm_appendixsubsec (),
  562.   cm_subsubsection (), cm_unnumberedsubsubsec (), cm_appendixsubsubsec (),
  563.   cm_heading (), cm_chapheading (), cm_subheading (), cm_subsubheading (),
  564.   cm_majorheading (), cm_raisesections (), cm_lowersections ();
  565.  
  566. /* All @defxxx commands map to cm_defun (). */
  567. int cm_defun ();
  568.  
  569. int
  570.   cm_node (), cm_menu (), cm_xref (), cm_ftable (), cm_vtable (), cm_pxref (),
  571.   cm_inforef (), cm_quotation (), cm_display (), cm_itemize (),
  572.   cm_enumerate (), cm_table (), cm_itemx (), cm_noindent (), cm_setfilename (),
  573.   cm_br (), cm_sp (), cm_page (), cm_group (), cm_center (), cm_include (),
  574.   cm_bye (), cm_item (), cm_end (), cm_infoinclude (), cm_ifinfo (),
  575.   cm_kindex (), cm_cindex (), cm_findex (), cm_pindex (), cm_vindex (),
  576.   cm_tindex (), cm_asis (), cm_synindex (), cm_printindex (), cm_minus (),
  577.   cm_footnote (), cm_force_abbreviated_whitespace (), cm_example (),
  578.   cm_smallexample (), cm_lisp (), cm_format (), cm_exdent (), cm_defindex (),
  579.   cm_defcodeindex (), cm_sc (), cm_result (), cm_expansion (), cm_equiv (),
  580.   cm_print (), cm_error (), cm_point (), cm_today (), cm_flushleft (),
  581.   cm_flushright (), cm_smalllisp (), cm_finalout (), cm_math (),
  582.   cm_cartouche (), cm_ignore_sentence_ender ();
  583.  
  584. /* Conditionals. */
  585. int cm_set (), cm_clear (), cm_ifset (), cm_ifclear (), cm_value ();
  586.  
  587. #if defined (HAVE_MACROS)
  588. /* Define a user-defined command which is simple substitution. */
  589. int cm_macro (), cm_unmacro ();
  590. #endif /* HAVE_MACROS */
  591.  
  592. /* Options. */
  593. int cm_paragraphindent (), cm_footnotestyle ();
  594.  
  595. /* Internals. */
  596. int do_nothing (), command_name_condition ();
  597. int misplaced_brace (), cm_obsolete ();
  598. #endif /* NOT_USED */
  599.  
  600. typedef struct
  601. {
  602.   const char *name;
  603.   FUNCTION *proc;
  604.   int argument_in_braces;
  605. } COMMAND;
  606.  
  607. /* Stuff for defining commands on the fly. */
  608. COMMAND **user_command_array = (COMMAND **) NULL;
  609. int user_command_array_len = 0;
  610.  
  611. /* other typedefs  - we may need them for prototypes */
  612.  
  613. typedef struct generic_list {
  614.   struct generic_list *next;
  615. } GENERIC_LIST;
  616.  
  617. enum insertion_type { menu, quotation, lisp, smalllisp, example,
  618.   smallexample, display, itemize, format, enumerate, cartouche, table,
  619.   ftable, vtable, group, ifinfo, flushleft, flushright, ifset, ifclear, deffn,
  620.   defun, defmac, defspec, defvr, defvar, defopt, deftypefn,
  621.   deftypefun, deftypevr, deftypevar, defcv, defivar, defop, defmethod,
  622.   deftypemethod, deftp, bad_type };
  623.  
  624. typedef struct istack_elt
  625. {
  626.   struct istack_elt *next;
  627.   char *item_function;
  628.   int line_number;
  629.   int filling_enabled;
  630.   int indented_fill;
  631.   enum insertion_type insertion;
  632.   int inhibited;
  633. } INSERTION_ELT;
  634.  
  635. #define max_stack_depth 100
  636. #define ENUM_DIGITS 1
  637. #define ENUM_ALPHA  2
  638. typedef struct {
  639.   int enumtype;
  640.   int enumval;
  641. } DIGIT_ALPHA;
  642.  
  643. /* A structure which contains `defined' variables. */
  644. typedef struct _defines {
  645.   struct _defines *next;
  646.   char *name;
  647.   char *value;
  648. } DEFINE;
  649.  
  650.  
  651. /* An index element... */
  652. typedef struct index_elt
  653. {
  654.   struct index_elt *next;
  655.   char *entry;            /* The index entry itself. */
  656.   char *node;            /* The node from whence it came. */
  657.   int code;            /* Non-zero means add `@code{...}' when
  658.                    printing this element. */
  659.   size_t defining_line;        /* Line number where this entry was written. */
  660. } INDEX_ELT;
  661.  
  662. /* A list of short-names for each index, and the index to that index in our
  663.    index array, the_indices.  In addition, for each index, it is remembered
  664.    whether that index is a code index or not.  Code indices have @code{}
  665.    inserted around the first word when they are printed with printindex. */
  666. typedef struct
  667. {
  668.   char *name;
  669.   int index;
  670.   int code;
  671. } INDEX_ALIST;
  672.  
  673.  
  674. INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL;
  675.  
  676. /* An array of pointers.  Each one is for a different index.  The
  677.    "synindex" command changes which array slot is pointed to by a
  678.    given "index". */
  679. INDEX_ELT **the_indices = (INDEX_ELT **) NULL;
  680.  
  681. /* We predefine these. */
  682. #define program_index 0
  683. #define function_index 1
  684. #define concept_index 2
  685. #define variable_index 3
  686. #define datatype_index 4
  687. #define key_index 5
  688.  
  689. /* The number of defined indices. */
  690. int defined_indices = 0;
  691.  
  692. typedef struct fn
  693. {
  694.   struct fn *next;
  695.   char *marker;
  696.   char *note;
  697. }  FN;
  698.  
  699. struct token_accumulator
  700. {
  701.   unsigned int length;
  702.   unsigned int index;
  703.   char **tokens;
  704. };
  705.  
  706.  
  707. int main __PROTO((int argc, char **argv));
  708. void print_version_info __PROTO((void));
  709. void *xmalloc __PROTO((size_t nbytes));
  710. void *xrealloc __PROTO((void *pointer, size_t nbytes));
  711. void memory_error __PROTO((const char *callers_name, size_t bytes_wanted));
  712. void usage __PROTO((void));
  713. int get_terminal_rows __PROTO((void));
  714. GENERIC_LIST *reverse_list __PROTO((GENERIC_LIST *list));
  715. char *find_and_load __PROTO((char *filename));
  716. void pushfile __PROTO((void));
  717. void popfile __PROTO((void));
  718. void flush_file_stack __PROTO((void));
  719. void push_node_filename __PROTO((void));
  720. void pop_node_filename __PROTO((void));
  721. char *filename_part __PROTO((char *filename));
  722. char *pathname_part __PROTO((char *filename));
  723. char *expand_filename __PROTO((char *filename, const char *input_name));
  724. char *full_pathname __PROTO((char *filename));
  725. void fs_error __PROTO((char *filename));
  726. void error __PROTO((const char *format, ...));
  727. void line_error __PROTO((const char *format, ...));
  728. void warning __PROTO((const char *format, ...));
  729. void remember_error __PROTO((void));
  730. char *read_token __PROTO((void));
  731. int self_delimiting __PROTO((int character));
  732. void canon_white __PROTO((char *string));
  733. void fix_whitespace __PROTO((char *string));
  734. void discard_until __PROTO((const char *string));
  735. size_t get_until __PROTO((const char *match, char **string));
  736. void get_until_in_line __PROTO((const char *match, char **string));
  737. void get_rest_of_line __PROTO((char **string));
  738. void backup_input_pointer __PROTO((void));
  739. void get_until_in_braces __PROTO((const char *match, char **string));
  740. void convert __PROTO((char *name));
  741. void free_and_clear __PROTO((char **pointer));
  742. void init_internals __PROTO((void));
  743. void init_paragraph __PROTO((void));
  744. void init_par_parameters __PROTO((void));
  745. void reader_loop __PROTO((void));
  746. COMMAND *get_command_entry __PROTO((char *string));
  747. void read_command __PROTO((void));
  748. const char *find_proc_name __PROTO((FUNCTION *proc));
  749. void init_brace_stack __PROTO((void));
  750. void remember_brace __PROTO((FUNCTION *proc));
  751. void remember_brace_1 __PROTO((FUNCTION *proc, size_t position));
  752. void pop_and_call_brace __PROTO((void));
  753. void discard_braces __PROTO((void));
  754. int get_char_len __PROTO((unsigned int character));
  755. void add_word_args __PROTO((const char *format, ...));
  756. void add_word __PROTO((const char *string));
  757. void add_char __PROTO((unsigned int character));
  758. void insert __PROTO((unsigned int character));
  759. void kill_self_indent __PROTO((int count));
  760. void inhibit_output_flushing __PROTO((void));
  761. void uninhibit_output_flushing __PROTO((void));
  762. void flush_output __PROTO((void));
  763. void close_single_paragraph __PROTO((void));
  764. void close_insertion_paragraph __PROTO((void));
  765. void close_paragraph_with_lines __PROTO((int lines));
  766. void close_paragraph __PROTO((void));
  767. void ignore_blank_line __PROTO((void));
  768. void do_flush_right_indentation __PROTO((void));
  769. void start_paragraph __PROTO((void));
  770. void indent __PROTO((int amount));
  771. size_t search_forward __PROTO((const char *string, size_t from));
  772. int stricmp __PROTO((const char *string1, const char *string2));
  773. void init_insertion_stack __PROTO((void));
  774. enum insertion_type current_insertion_type __PROTO((void));
  775. char *current_item_function __PROTO((void));
  776. char *get_item_function __PROTO((void));
  777. void push_insertion __PROTO((enum insertion_type type, char *item_function));
  778. void pop_insertion __PROTO((void));
  779. const char *insertion_type_pname __PROTO((enum insertion_type type));
  780. enum insertion_type find_type_from_name __PROTO((char *name));
  781. void do_nothing __PROTO((void));
  782. int defun_insertion __PROTO((enum insertion_type type));
  783. void start_enumerating __PROTO((int at, int type));
  784. void stop_enumerating __PROTO((void));
  785. void enumerate_item __PROTO((void));
  786. void begin_insertion __PROTO((enum insertion_type type));
  787. void end_insertion __PROTO((enum insertion_type type));
  788. void discard_insertions __PROTO((void));
  789. void insert_self __PROTO((void));
  790. void cm_asterisk __PROTO((void));
  791. void cm_dots __PROTO((int arg));
  792. void cm_bullet __PROTO((int arg));
  793. void cm_minus __PROTO((int arg));
  794. void cm_TeX __PROTO((int arg));
  795. void cm_copyright __PROTO((int arg));
  796. void cm_today __PROTO((int arg));
  797. void cm_code __PROTO((int arg));
  798. void cm_samp __PROTO((int arg));
  799. void cm_file __PROTO((int arg));
  800. void cm_kbd __PROTO((int arg));
  801. void cm_key __PROTO((int arg));
  802. void cm_ctrl __PROTO((int arg, size_t position));
  803. void cm_sc __PROTO((int arg, size_t start_pos, size_t end_pos));
  804. void cm_var __PROTO((int arg, size_t start_pos, size_t end_pos));
  805. void cm_dfn __PROTO((int arg, size_t position));
  806. void cm_emph __PROTO((int arg));
  807. void cm_strong __PROTO((int arg, size_t position));
  808. void cm_cite __PROTO((int arg, size_t position));
  809. void cm_italic __PROTO((int arg, int start, int end));
  810. void cm_bold __PROTO((int arg, int start, int end));
  811. void cm_roman __PROTO((int arg, int start, int end));
  812. void cm_titlefont __PROTO((int arg, int start, int end));
  813. void cm_title __PROTO((int arg, int start, int end));
  814. void cm_refill __PROTO((void));
  815. void cm_w __PROTO((int arg, int start, int end));
  816. void cm_obsolete __PROTO((int arg, int start, int end));
  817. void insert_and_underscore __PROTO((int with_char));
  818. void cm_raisesections __PROTO((void));
  819. void cm_lowersections __PROTO((void));
  820. int what_section __PROTO((char *text));
  821. void cm_top __PROTO((void));
  822. void sectioning_underscore __PROTO((char *command));
  823. void cm_chapter __PROTO((void));
  824. void cm_section __PROTO((void));
  825. void cm_subsection __PROTO((void));
  826. void cm_subsubsection __PROTO((void));
  827. void cm_unnumbered __PROTO((void));
  828. void cm_unnumberedsec __PROTO((void));
  829. void cm_unnumberedsubsec __PROTO((void));
  830. void cm_unnumberedsubsubsec __PROTO((void));
  831. void cm_appendix __PROTO((void));
  832. void cm_appendixsec __PROTO((void));
  833. void cm_appendixsubsec __PROTO((void));
  834. void cm_appendixsubsubsec __PROTO((void));
  835. void cm_majorheading __PROTO((void));
  836. void cm_chapheading __PROTO((void));
  837. void cm_heading __PROTO((void));
  838. void cm_subheading __PROTO((void));
  839. void cm_subsubheading __PROTO((void));
  840. void init_tag_table __PROTO((void));
  841. void write_tag_table __PROTO((void));
  842. void write_tag_table_indirect __PROTO((void));
  843. void write_tag_table_internal __PROTO((int indirect_p));
  844. char *get_node_token __PROTO((void));
  845. void normalize_node_name __PROTO((char *string));
  846. TAG_ENTRY *find_node __PROTO((char *name));
  847. void remember_node __PROTO((char *node, char *prev, char *next,
  848.                 char *up, size_t position, size_t line_no,
  849.                 int no_warn));
  850. void cm_node __PROTO((void));
  851. void validate_file __PROTO((char *filename, TAG_ENTRY *tag_table));
  852. int validate __PROTO((char *tag, size_t line, const char *label));
  853. void split_file __PROTO((char *filename, size_t size));
  854. const char *reftype_type_string __PROTO((enum reftype type));
  855. void remember_node_reference __PROTO((char *node, size_t line,
  856.                       enum reftype type));
  857. void validate_other_references __PROTO((NODE_REF *ref_list));
  858. NODE_REF *find_node_reference __PROTO((char *node, NODE_REF *ref_list));
  859. void free_node_references __PROTO((void));
  860. char *glean_node_from_menu __PROTO((int remember_reference));
  861. void cm_menu __PROTO((void));
  862. char *get_xref_token __PROTO((void));
  863. void cm_xref __PROTO((int arg));
  864. void cm_pxref __PROTO((int arg));
  865. void cm_inforef __PROTO((int arg));
  866. void cm_quotation __PROTO((void));
  867. void cm_example __PROTO((void));
  868. void cm_smallexample __PROTO((void));
  869. void cm_lisp __PROTO((void));
  870. void cm_smalllisp __PROTO((void));
  871. void cm_cartouche __PROTO((void));
  872. void cm_format __PROTO((void));
  873. void cm_display __PROTO((void));
  874. void cm_itemize __PROTO((void));
  875. void cm_enumerate __PROTO((void));
  876. void do_enumeration __PROTO((int type, const char *default_string));
  877. void cm_table __PROTO((void));
  878. void cm_ftable __PROTO((void));
  879. void cm_vtable __PROTO((void));
  880. void cm_group __PROTO((void));
  881. void cm_ifinfo __PROTO((void));
  882. void cm_flushleft __PROTO((void));
  883. void cm_flushright __PROTO((void));
  884. void set __PROTO((char *name, char *value));
  885. void clear __PROTO((char *name));
  886. char *set_p __PROTO((char *name));
  887. void command_name_condition __PROTO((void));
  888. void cm_set __PROTO((void));
  889. void cm_clear __PROTO((void));
  890. void cm_ifset __PROTO((void));
  891. void cm_ifclear __PROTO((void));
  892. void cm_value __PROTO((int arg, size_t start_pos, size_t end_pos));
  893. void handle_variable __PROTO((int action));
  894. void handle_variable_internal __PROTO((int action, char *name));
  895. void execute_string __PROTO((const char *format, ...));
  896. void cm_itemx __PROTO((void));
  897. void cm_item __PROTO((void));
  898. void initialize_token_accumulator __PROTO((struct token_accumulator
  899.                        *accumulator));
  900. void accumulate_token __PROTO((struct token_accumulator *accumulator,
  901.                    char *token));
  902. char *copy_substring __PROTO((char *start, char *end));
  903. int scan_group_in_string __PROTO((char **string_pointer));
  904. char **args_from_string __PROTO((char *string));
  905. void process_defun_args __PROTO((char **defun_args, int auto_var_p));
  906. char *next_nonwhite_defun_arg __PROTO((char ***arg_pointer));
  907. void defun_internal __PROTO((enum insertion_type type, int x_p));
  908. void cm_defun __PROTO((void));
  909. void cm_end __PROTO((void));
  910. void cm_noindent __PROTO((void));
  911. void cm_setfilename __PROTO((void));
  912. void cm_ignore_line __PROTO((void));
  913. void cm_br __PROTO((void));
  914. void cm_sp __PROTO((void));
  915. void cm_center __PROTO((void));
  916. void cm_result __PROTO((int arg));
  917. void cm_expansion __PROTO((int arg));
  918. void cm_equiv __PROTO((int arg));
  919. void cm_print __PROTO((int arg));
  920. void cm_error __PROTO((int arg));
  921. void cm_point __PROTO((int arg));
  922. void cm_exdent __PROTO((void));
  923. void cm_include __PROTO((void));
  924. void cm_infoinclude __PROTO((void));
  925. void misplaced_brace __PROTO((void));
  926. void cm_force_abbreviated_whitespace __PROTO((void));
  927. void cm_ignore_sentence_ender __PROTO((void));
  928. void cm_bye __PROTO((void));
  929. void cm_asis __PROTO((void));
  930. void cm_math __PROTO((void));
  931. void init_indices __PROTO((void));
  932. int find_index_offset __PROTO((const char *name));
  933. INDEX_ALIST *find_index __PROTO((const char *name));
  934. int translate_index __PROTO((const char *name));
  935. INDEX_ELT *index_list __PROTO((const char *name));
  936. void free_index __PROTO((INDEX_ELT *index));
  937. void undefindex __PROTO((const char *name));
  938. void defindex __PROTO((const char *name, int code));
  939. void index_add_arg __PROTO((const char *name));
  940. void gen_index __PROTO((void));
  941. void cm_defindex __PROTO((void));
  942. void cm_defcodeindex __PROTO((void));
  943. void gen_defindex __PROTO((int code));
  944. INDEX_ELT *index_append __PROTO((INDEX_ELT *head, INDEX_ELT *tail));
  945. void cm_synindex __PROTO((void));
  946. void cm_pindex __PROTO((void));
  947. void cm_vindex __PROTO((void));
  948. void cm_kindex __PROTO((void));
  949. void cm_cindex __PROTO((void));
  950. void cm_findex __PROTO((void));
  951. void cm_tindex __PROTO((void));
  952. int index_element_compare __PROTO((INDEX_ELT **element1,
  953.                    INDEX_ELT **element2));
  954. INDEX_ELT **sort_index __PROTO((INDEX_ELT *index));
  955. void cm_printindex __PROTO((void));
  956. void define_user_command __PROTO((char *name,
  957.                   FUNCTION *proc, int needs_braces_p));
  958. void define_alias __PROTO((char *alias, char *function));
  959. int set_paragraph_indent __PROTO((char *string));
  960. void cm_paragraphindent __PROTO((void));
  961. int set_footnote_style __PROTO((char *string));
  962. void cm_footnotestyle __PROTO((void));
  963. void remember_note __PROTO((char *marker, char *note));
  964. void free_pending_notes __PROTO((void));
  965. void cm_footnote __PROTO((void));
  966. void output_pending_notes __PROTO((void));
  967. #if defined (HAVE_MACROS)
  968. MACRO_DEF *find_macro __PROTO((char *name));
  969. void add_macro __PROTO((char *name, char *definition,
  970.             char *filename, size_t lineno));
  971. MACRO_DEF *delete_macro __PROTO((char *name));
  972. void execute_macro __PROTO((MACRO_DEF *def));
  973. void cm_macro __PROTO((void));
  974. void cm_unmacro __PROTO((void));
  975. #endif /* HAVE_MACROS */
  976. char *extract_colon_unit __PROTO((char *string, int *index));
  977. char *get_file_info_in_path __PROTO((char *filename, char *path,
  978.                      struct stat *finfo));
  979. TAG_ENTRY *restore_tag_table __PROTO((char *the_file, size_t size));
  980.  
  981. #undef __PROTO
  982.  
  983. #define NO_BRACE_ARGS 0
  984. #define BRACE_ARGS 1
  985.  
  986. static COMMAND CommandTable[] = {
  987.   { "!", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  988.   { "'", insert_self, NO_BRACE_ARGS },
  989.   { "*", cm_asterisk, NO_BRACE_ARGS },
  990.   { ".", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  991.   { ":", cm_force_abbreviated_whitespace, NO_BRACE_ARGS },
  992.   { "?", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  993.   { "|", do_nothing, NO_BRACE_ARGS },
  994.   { "@", insert_self, NO_BRACE_ARGS },
  995.   { " ", insert_self, NO_BRACE_ARGS },
  996.   { "\n", insert_self, NO_BRACE_ARGS },
  997.   { "TeX", cm_TeX, BRACE_ARGS },
  998.   { "`", insert_self, NO_BRACE_ARGS },
  999.   { "appendix", cm_appendix, NO_BRACE_ARGS },
  1000.   { "appendixsection", cm_appendixsec, NO_BRACE_ARGS },
  1001.   { "appendixsec", cm_appendixsec, NO_BRACE_ARGS },
  1002.   { "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
  1003.   { "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
  1004.   { "asis", cm_asis, BRACE_ARGS },
  1005.   { "b", cm_bold, BRACE_ARGS },
  1006.   { "br", cm_br, NO_BRACE_ARGS },
  1007.   { "bullet", cm_bullet, BRACE_ARGS },
  1008.   { "bye", cm_bye, NO_BRACE_ARGS },
  1009.   { "c", cm_ignore_line, NO_BRACE_ARGS },
  1010.   { "cartouche", cm_cartouche, NO_BRACE_ARGS },
  1011.   { "center", cm_center, NO_BRACE_ARGS },
  1012.   { "chapheading", cm_chapheading, NO_BRACE_ARGS },
  1013.   { "chapter", cm_chapter, NO_BRACE_ARGS },
  1014.   { "cindex", cm_cindex, NO_BRACE_ARGS },
  1015.   { "cite", cm_cite, BRACE_ARGS },
  1016.   { "clear", cm_clear, NO_BRACE_ARGS },
  1017.   { "code", cm_code, BRACE_ARGS },
  1018.   { "comment", cm_ignore_line, NO_BRACE_ARGS },
  1019.   { "contents", do_nothing, NO_BRACE_ARGS },
  1020.   { "copyright", cm_copyright, BRACE_ARGS },
  1021.   { "ctrl", cm_ctrl, BRACE_ARGS },
  1022.   { "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS },
  1023.   { "defindex", cm_defindex, NO_BRACE_ARGS },
  1024.   { "dfn", cm_dfn, BRACE_ARGS },
  1025.  
  1026. /* The `def' commands. */
  1027.   { "deffn", cm_defun, NO_BRACE_ARGS },
  1028.   { "deffnx", cm_defun, NO_BRACE_ARGS },
  1029.   { "defun", cm_defun, NO_BRACE_ARGS },
  1030.   { "defunx", cm_defun, NO_BRACE_ARGS },
  1031.   { "defmac", cm_defun, NO_BRACE_ARGS },
  1032.   { "defmacx", cm_defun, NO_BRACE_ARGS },
  1033.   { "defspec", cm_defun, NO_BRACE_ARGS },
  1034.   { "defspecx", cm_defun, NO_BRACE_ARGS },
  1035.   { "defvr", cm_defun, NO_BRACE_ARGS },
  1036.   { "defvrx", cm_defun, NO_BRACE_ARGS },
  1037.   { "defvar", cm_defun, NO_BRACE_ARGS },
  1038.   { "defvarx", cm_defun, NO_BRACE_ARGS },
  1039.   { "defopt", cm_defun, NO_BRACE_ARGS },
  1040.   { "defoptx", cm_defun, NO_BRACE_ARGS },
  1041.   { "deftypefn", cm_defun, NO_BRACE_ARGS },
  1042.   { "deftypefnx", cm_defun, NO_BRACE_ARGS },
  1043.   { "deftypefun", cm_defun, NO_BRACE_ARGS },
  1044.   { "deftypefunx", cm_defun, NO_BRACE_ARGS },
  1045.   { "deftypevr", cm_defun, NO_BRACE_ARGS },
  1046.   { "deftypevrx", cm_defun, NO_BRACE_ARGS },
  1047.   { "deftypevar", cm_defun, NO_BRACE_ARGS },
  1048.   { "deftypevarx", cm_defun, NO_BRACE_ARGS },
  1049.   { "defcv", cm_defun, NO_BRACE_ARGS },
  1050.   { "defcvx", cm_defun, NO_BRACE_ARGS },
  1051.   { "defivar", cm_defun, NO_BRACE_ARGS },
  1052.   { "defivarx", cm_defun, NO_BRACE_ARGS },
  1053.   { "defop", cm_defun, NO_BRACE_ARGS },
  1054.   { "defopx", cm_defun, NO_BRACE_ARGS },
  1055.   { "defmethod", cm_defun, NO_BRACE_ARGS },
  1056.   { "defmethodx", cm_defun, NO_BRACE_ARGS },
  1057.   { "deftypemethod", cm_defun, NO_BRACE_ARGS },
  1058.   { "deftypemethodx", cm_defun, NO_BRACE_ARGS },
  1059.   { "deftp", cm_defun, NO_BRACE_ARGS },
  1060.   { "deftpx", cm_defun, NO_BRACE_ARGS },
  1061. /* The end of the `def' commands. */
  1062.  
  1063.   { "display", cm_display, NO_BRACE_ARGS },
  1064.   { "dots", cm_dots, BRACE_ARGS },
  1065.   { "dmn", do_nothing, BRACE_ARGS },
  1066.   { "emph", cm_emph, BRACE_ARGS },
  1067.   { "end", cm_end, NO_BRACE_ARGS },
  1068.   { "enumerate", cm_enumerate, NO_BRACE_ARGS },
  1069.   { "equiv", cm_equiv, BRACE_ARGS },
  1070.   { "error", cm_error, BRACE_ARGS },
  1071.   { "example", cm_example, NO_BRACE_ARGS },
  1072.   { "exdent", cm_exdent, NO_BRACE_ARGS },
  1073.   { "expansion", cm_expansion, BRACE_ARGS },
  1074.   { "file", cm_file, BRACE_ARGS },
  1075.   { "findex", cm_findex, NO_BRACE_ARGS },
  1076.   { "finalout", do_nothing, NO_BRACE_ARGS },
  1077.   { "flushleft", cm_flushleft, NO_BRACE_ARGS },
  1078.   { "flushright", cm_flushright, NO_BRACE_ARGS },
  1079.   { "format", cm_format, NO_BRACE_ARGS },
  1080.   { "ftable", cm_ftable, NO_BRACE_ARGS },
  1081.   { "group", cm_group, NO_BRACE_ARGS },
  1082.   { "heading", cm_heading, NO_BRACE_ARGS },
  1083.   { "headings", cm_ignore_line, NO_BRACE_ARGS },
  1084.   { "i", cm_italic, BRACE_ARGS },
  1085.   { "iappendix", cm_appendix, NO_BRACE_ARGS },
  1086.   { "iappendixsection", cm_appendixsec, NO_BRACE_ARGS },
  1087.   { "iappendixsec", cm_appendixsec, NO_BRACE_ARGS },
  1088.   { "iappendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
  1089.   { "iappendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
  1090.   { "ichapter", cm_chapter, NO_BRACE_ARGS },
  1091.   { "ifclear", cm_ifclear, NO_BRACE_ARGS },
  1092.   { "ifinfo", cm_ifinfo, NO_BRACE_ARGS },
  1093.   { "ifset", cm_ifset, NO_BRACE_ARGS },
  1094.   { "iftex", command_name_condition, NO_BRACE_ARGS },
  1095.   { "ignore", command_name_condition, NO_BRACE_ARGS },
  1096.   { "include", cm_include, NO_BRACE_ARGS },
  1097.   { "inforef", cm_inforef, BRACE_ARGS },
  1098.   { "input", cm_include, NO_BRACE_ARGS },
  1099.   { "isection", cm_section, NO_BRACE_ARGS },
  1100.   { "isubsection", cm_subsection, NO_BRACE_ARGS },
  1101.   { "isubsubsection", cm_subsubsection, NO_BRACE_ARGS },
  1102.   { "item", cm_item, NO_BRACE_ARGS },
  1103.   { "itemize", cm_itemize, NO_BRACE_ARGS },
  1104.   { "itemx", cm_itemx, NO_BRACE_ARGS },
  1105.   { "iunnumbered", cm_unnumbered, NO_BRACE_ARGS },
  1106.   { "iunnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
  1107.   { "iunnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
  1108.   { "iunnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
  1109.   { "kbd", cm_kbd, BRACE_ARGS },
  1110.   { "key", cm_key, BRACE_ARGS },
  1111.   { "kindex", cm_kindex, NO_BRACE_ARGS },
  1112.   { "lowersections", cm_lowersections, NO_BRACE_ARGS },
  1113.   { "lisp", cm_lisp, NO_BRACE_ARGS },
  1114.   { "macro", cm_macro, NO_BRACE_ARGS },
  1115.   { "majorheading", cm_majorheading, NO_BRACE_ARGS },
  1116.   { "math", cm_math, BRACE_ARGS },
  1117.   { "menu", cm_menu, NO_BRACE_ARGS },
  1118.   { "minus", cm_minus, BRACE_ARGS },
  1119.   { "need", cm_ignore_line, NO_BRACE_ARGS },
  1120.   { "node", cm_node, NO_BRACE_ARGS },
  1121.   { "noindent", cm_noindent, NO_BRACE_ARGS },
  1122.   { "nwnode", cm_node, NO_BRACE_ARGS },
  1123.   { "overfullrule", cm_ignore_line, NO_BRACE_ARGS },
  1124.   { "page", do_nothing, NO_BRACE_ARGS },
  1125.   { "pindex", cm_pindex, NO_BRACE_ARGS },
  1126.   { "point", cm_point, BRACE_ARGS },
  1127.   { "print", cm_print, BRACE_ARGS },
  1128.   { "printindex", cm_printindex, NO_BRACE_ARGS },
  1129.   { "pxref", cm_pxref, BRACE_ARGS },
  1130.   { "quotation", cm_quotation, NO_BRACE_ARGS },
  1131.   { "r", cm_roman, BRACE_ARGS },
  1132.   { "raisesections", cm_raisesections, NO_BRACE_ARGS },
  1133.   { "ref", cm_xref, BRACE_ARGS },
  1134.   { "refill", cm_refill, NO_BRACE_ARGS },
  1135.   { "result", cm_result, BRACE_ARGS },
  1136.   { "samp", cm_samp, BRACE_ARGS },
  1137.   { "sc", cm_sc, BRACE_ARGS },
  1138.   { "section", cm_section, NO_BRACE_ARGS },
  1139.   { "set", cm_set, NO_BRACE_ARGS },
  1140.   { "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS },
  1141.   { "setchapterstyle", cm_ignore_line, NO_BRACE_ARGS },
  1142.   { "setfilename", cm_setfilename, NO_BRACE_ARGS },
  1143.   { "settitle", cm_ignore_line, NO_BRACE_ARGS },
  1144.   { "shortcontents", do_nothing, NO_BRACE_ARGS },
  1145.   { "shorttitlepage", command_name_condition, NO_BRACE_ARGS },
  1146.   { "smallbook", cm_ignore_line, NO_BRACE_ARGS },
  1147.   { "smallexample", cm_smallexample, NO_BRACE_ARGS },
  1148.   { "smalllisp", cm_smalllisp, NO_BRACE_ARGS },
  1149.   { "sp", cm_sp, NO_BRACE_ARGS },
  1150.   { "strong", cm_strong, BRACE_ARGS },
  1151.   { "subheading", cm_subheading, NO_BRACE_ARGS },
  1152.   { "subsection", cm_subsection, NO_BRACE_ARGS },
  1153.   { "subsubheading", cm_subsubheading, NO_BRACE_ARGS },
  1154.   { "subsubsection", cm_subsubsection, NO_BRACE_ARGS },
  1155.   { "summarycontents", do_nothing, NO_BRACE_ARGS },
  1156.   { "syncodeindex", cm_synindex, NO_BRACE_ARGS },
  1157.   { "synindex", cm_synindex, NO_BRACE_ARGS },
  1158.   { "t", cm_title, BRACE_ARGS },
  1159.   { "table", cm_table, NO_BRACE_ARGS },
  1160.   { "tex", command_name_condition, NO_BRACE_ARGS },
  1161.   { "tindex", cm_tindex, NO_BRACE_ARGS },
  1162.   { "titlefont", cm_titlefont, BRACE_ARGS },
  1163.   { "titlepage", command_name_condition, NO_BRACE_ARGS },
  1164.   { "titlespec", command_name_condition, NO_BRACE_ARGS },
  1165.   { "today", cm_today, BRACE_ARGS },
  1166.   { "top", cm_top, NO_BRACE_ARGS  },
  1167.   { "unmacro", cm_unmacro, NO_BRACE_ARGS },
  1168.   { "unnumbered", cm_unnumbered, NO_BRACE_ARGS },
  1169.   { "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
  1170.   { "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
  1171.   { "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
  1172.   { "value", cm_value, BRACE_ARGS },
  1173.   { "var", cm_var, BRACE_ARGS },
  1174.   { "vindex", cm_vindex, NO_BRACE_ARGS },
  1175.   { "vtable", cm_vtable, NO_BRACE_ARGS },
  1176.   { "w", cm_w, BRACE_ARGS },
  1177.   { "xref", cm_xref, BRACE_ARGS },
  1178.   { "{", insert_self, NO_BRACE_ARGS },
  1179.   { "}", insert_self, NO_BRACE_ARGS },
  1180.  
  1181.   /* Some obsoleted commands. */
  1182.   { "infotop", cm_obsolete, NO_BRACE_ARGS },
  1183.   { "infounnumbered", cm_obsolete, NO_BRACE_ARGS },
  1184.   { "infounnumberedsec", cm_obsolete, NO_BRACE_ARGS },
  1185.   { "infounnumberedsubsec", cm_obsolete, NO_BRACE_ARGS },
  1186.   { "infounnumberedsubsubsec", cm_obsolete, NO_BRACE_ARGS },
  1187.   { "infoappendix", cm_obsolete, NO_BRACE_ARGS },
  1188.   { "infoappendixsec", cm_obsolete, NO_BRACE_ARGS },
  1189.   { "infoappendixsubsec", cm_obsolete, NO_BRACE_ARGS },
  1190.   { "infoappendixsubsubsec", cm_obsolete, NO_BRACE_ARGS },
  1191.   { "infochapter", cm_obsolete, NO_BRACE_ARGS },
  1192.   { "infosection", cm_obsolete, NO_BRACE_ARGS },
  1193.   { "infosubsection", cm_obsolete, NO_BRACE_ARGS },
  1194.   { "infosubsubsection", cm_obsolete, NO_BRACE_ARGS },
  1195.  
  1196.   /* Now @include does what this was supposed to. */
  1197.   { "infoinclude", cm_infoinclude, NO_BRACE_ARGS },
  1198.   { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */
  1199.   { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS },
  1200.   { "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS },
  1201.  
  1202.   {(char *) NULL, (FUNCTION *) NULL, NO_BRACE_ARGS}};
  1203.  
  1204. int major_version = 1;
  1205. int minor_version = 55;
  1206.  
  1207. struct option long_options[] =
  1208. {
  1209.   { "error-limit", 1, 0, 'e' },            /* formerly -el */
  1210.   { "fill-column", 1, 0, 'f' },            /* formerly -fc */
  1211.   { "footnote-style", 1, 0, 's' },        /* formerly -ft */
  1212.   { "no-headers", 0, &no_headers, 1 },        /* Do not output Node: foo */
  1213.   { "no-pointer-validate", 0, &validating, 0 }, /* formerly -nv */
  1214.   { "no-validate", 0, &validating, 0 },        /* formerly -nv */
  1215.   { "no-split", 0, &splitting, 0 },        /* formerly -ns */
  1216.   { "only-split", 0, &only_split, 1 },
  1217.   { "no-warn", 0, &print_warnings, 0 },        /* formerly -nw */
  1218.   { "number-footnotes", 0, &number_footnotes, 1 },
  1219.   { "no-number-footnotes", 0, &number_footnotes, 0 },
  1220.   { "output", 1, 0, 'o' },
  1221.   { "paragraph-indent", 1, 0, 'p' },        /* formerly -pi */
  1222.   { "reference-limit", 1, 0, 'r' },        /* formerly -rl */
  1223.   { "verbose", 0, &verbose_mode, 1 },        /* formerly -verbose */
  1224.   { "version", 0, 0, 'V' },
  1225.   {NULL, 0, NULL, 0}
  1226. };
  1227.  
  1228. /* Values for calling handle_variable_internal (). */
  1229. #define SET    1
  1230. #define CLEAR    2
  1231. #define IFSET    3
  1232. #define IFCLEAR    4
  1233.  
  1234. #ifndef atarist
  1235. #define PATHSEP_STR  ":"
  1236. #else  /* atarist */
  1237. #define PATHSEP_STR  ","  /* colon is a valid path character */
  1238. #endif /* atarist */
  1239.  
  1240. /* **************************************************************** */
  1241. /*                                    */
  1242. /*            Main ()  Start of code              */
  1243. /*                                        */
  1244. /* **************************************************************** */
  1245.  
  1246. /* For each file mentioned in the command line, process it, turning
  1247.    texinfo commands into wonderfully formatted output text. */
  1248. int
  1249. main (argc, argv)
  1250.      int argc;
  1251.      char **argv;
  1252. {
  1253.   extern int errors_printed;
  1254.   char *filename_part ();
  1255.   int c, ind;
  1256.  
  1257.   /* The name of this program is the last filename in argv[0]. */
  1258.   progname = filename_part (argv[0]);
  1259.  
  1260.   /* Parse argument flags from the input line. */
  1261.   while ((c = getopt_long
  1262.       (argc, argv, "D:U:I:f:o:p:e:r:s:V", long_options, &ind))
  1263.      != EOF)
  1264.     {
  1265.       if (c == 0 && long_options[ind].flag == 0)
  1266.     c = long_options[ind].val;
  1267.  
  1268.       switch (c)
  1269.     {
  1270.       /* User specified variable to set or clear? */
  1271.     case 'D':
  1272.     case 'U':
  1273.       handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg);
  1274.       break;
  1275.  
  1276.       /* User specified include file path? */
  1277.     case 'I':
  1278.       if (!include_files_path)
  1279.         include_files_path = savestring (".");
  1280.  
  1281.       include_files_path = (char *)
  1282.         xrealloc (include_files_path,
  1283.               2 + strlen (include_files_path) + strlen (optarg));
  1284.       strcat (include_files_path, PATHSEP_STR);
  1285.       strcat (include_files_path, optarg);
  1286.       break;
  1287.  
  1288.       /* User specified fill_column? */
  1289.     case 'f':
  1290.       if (sscanf (optarg, "%d", &fill_column) != 1)
  1291.         usage ();
  1292.       break;
  1293.  
  1294.       /* User specified output file? */
  1295.     case 'o':
  1296.       command_output_filename = savestring (optarg);
  1297.       break;
  1298.  
  1299.       /* User specified paragraph indent (paragraph_start_index)? */
  1300.     case 'p':
  1301.       if (set_paragraph_indent (optarg) < 0)
  1302.         usage ();
  1303.       break;
  1304.  
  1305.       /* User specified error level? */
  1306.     case 'e':
  1307.       if (sscanf (optarg, "%d", &max_error_level) != 1)
  1308.         usage ();
  1309.       break;
  1310.  
  1311.       /* User specified reference warning limit? */
  1312.     case 'r':
  1313.       if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
  1314.         usage ();
  1315.       break;
  1316.  
  1317.       /* User specified footnote style? */
  1318.     case 's':
  1319.       if (set_footnote_style (optarg) < 0)
  1320.         usage ();
  1321.       footnote_style_preset = 1;
  1322.       break;
  1323.  
  1324.       /* User requested version info? */
  1325.     case 'V':
  1326.       print_version_info ();
  1327.       exit (NO_ERROR);
  1328.       break;
  1329.  
  1330.     case '?':
  1331.       usage ();
  1332.     }
  1333.     }
  1334.  
  1335.   if (optind == argc)
  1336.     usage ();
  1337.   else if (verbose_mode)
  1338.     print_version_info ();
  1339.  
  1340.   /* Remaining arguments are file names of texinfo files.
  1341.      Convert them, one by one. */
  1342.   while (optind != argc)
  1343.     convert (argv[optind++]);
  1344.  
  1345.   if (errors_printed)
  1346.     return (SYNTAX);
  1347.   else
  1348.     return (NO_ERROR);
  1349. }
  1350.  
  1351. #ifndef atarist
  1352. #define VERSION_STR    "This is GNU Makeinfo version %d.%d.\n"
  1353. #else  /* atarist */
  1354. #define VERSION_STR    "This is GNU Makeinfo version %d.%d (Atari ST).\n"
  1355. #endif  /* atarist */
  1356. /* Display the version info of this invocation of Makeinfo. */
  1357. void
  1358. print_version_info ()
  1359. {
  1360.   fprintf (stderr, VERSION_STR, major_version, minor_version);
  1361. }
  1362.  
  1363. /* **************************************************************** */
  1364. /*                                    */
  1365. /*            Generic Utilities                */
  1366. /*                                    */
  1367. /* **************************************************************** */
  1368.  
  1369. /* Just like malloc, but kills the program in case of fatal error. */
  1370. void *
  1371. xmalloc (nbytes)
  1372.      size_t nbytes;
  1373. {
  1374.   void *temp = (void *) malloc (nbytes);
  1375.  
  1376.   if (nbytes && temp == (void *)NULL)
  1377.     memory_error ("xmalloc", nbytes);
  1378.  
  1379.   return (temp);
  1380. }
  1381.  
  1382. /* Like realloc (), but barfs if there isn't enough memory. */
  1383. void *
  1384. xrealloc (pointer, nbytes)
  1385.      void *pointer;
  1386.      size_t nbytes;
  1387. {
  1388.   void *temp;
  1389.  
  1390.   if (!pointer)
  1391.     temp = (void *)xmalloc (nbytes);
  1392.   else
  1393.     temp = (void *)realloc (pointer, nbytes);
  1394.  
  1395.   if (nbytes && !temp)
  1396.     memory_error ("xrealloc", nbytes);
  1397.  
  1398.   return (temp);
  1399. }
  1400.  
  1401. void
  1402. memory_error (callers_name, bytes_wanted)
  1403.      const char *callers_name;
  1404.      size_t bytes_wanted;
  1405. {
  1406.   char printable_string[80];
  1407.  
  1408.   sprintf (printable_string,
  1409.        "Virtual memory exhausted in %s ()!  Needed %lu bytes.",
  1410.        callers_name, bytes_wanted);
  1411.  
  1412.   error (printable_string);
  1413.   abort ();
  1414. }
  1415.  
  1416.  
  1417. typedef struct
  1418. {
  1419.     const char *format;
  1420.     int        *val;
  1421. } HELP_ENTRY;
  1422.  
  1423. HELP_ENTRY help[] = {
  1424. {"This program accepts as input files of texinfo commands and text", 0},
  1425. {"and outputs a file suitable for reading with GNU Info.", 0},
  1426. {"", 0},
  1427. {"The options are:", 0},
  1428. {"`-I DIR'              to add DIR to the directory search list for including", 0},
  1429. {"\tfiles with the `@include' command.", 0},
  1430. {"`-D VAR'              to define a variable, as with `@set'.", 0},
  1431. {"`-U VAR'              to undefine a variable, as with `@clear'.", 0},
  1432. {"`--no-validate' or `--no-pointer-validate'", 0},
  1433. {"\tto suppress node cross reference validation.", 0},
  1434. {"`--no-warn'           to suppress warning messages (errors are still output).", 0},
  1435. {"`--no-split'          to suppress the splitting of large files.", 0},
  1436. {"`--only-split'        to split large file without remaking it.", 0},
  1437. {"`--no-headers'        to suppress the output of Node: Foo headers.", 0},
  1438. {"`--verbose'           to print information about what is being done.", 0},
  1439. {"`--version'           to print the version number of Makeinfo.", 0},
  1440. {"`--output FILE' or `-o FILE'", 0},
  1441. {"\tto specify the output file.  When you specify the", 0},
  1442. {"\toutput file in this way, any `@setfilename' in the", 0},
  1443. {"\tinput file is ignored.", 0},
  1444. {"`--paragraph-indent NUM'", 0},
  1445. {"\tto set the paragraph indent to NUM (default %d).",
  1446.      ¶graph_start_indent},
  1447. {"`--fill-column NUM'   to set the filling column to NUM (default %d).",
  1448.      &fill_column},
  1449. {"`--error-limit NUM'   to set the error limit to NUM (default %d).",
  1450.      &max_error_level},
  1451. {"`--reference-limit NUM'", 0},
  1452. {"\tto set the reference warning limit to NUM (default %d).",
  1453.      &reference_warning_limit},
  1454. {"`--number-footnotes'  do that if no exlicit marker was set (default).", 0},
  1455. {"`--no-number-footnotes'",0},
  1456. {"\tif no explicit marker then use '*'.", 0},
  1457. {"`--footnote-style STYLE'", 0},
  1458. {"\tto set the footnote style to STYLE.  STYLE should", 0},
  1459. {"\teither be `separate' to place footnotes in their own", 0},
  1460. {"\tnode, or `end', to place the footnotes at the end of", 0},
  1461. {"\tthe node in which they are defined (the default).\n", 0},
  1462. {NULL, 0}};
  1463.  
  1464. /* Tell the user how to use this program. */
  1465. void
  1466. usage ()
  1467. {
  1468.   HELP_ENTRY *h_entry = help;
  1469.   const char *str;
  1470.   int lines, rows = get_terminal_rows();
  1471.   
  1472.  
  1473.   fprintf (stderr, "Usage: %s [options] texinfo-file...\n\n", progname);
  1474.  
  1475.   lines = rows - 3;
  1476.   while (NULL != (str = h_entry->format)) {
  1477.     if (lines <= 0) {
  1478.       fprintf(stderr, "[ More... ]");
  1479.       lines = rows - 1;
  1480.       (void) getchar();
  1481.       fprintf(stderr, "\n");
  1482.     }
  1483.     if ('\t' == *str) {
  1484.       fprintf (stderr, "                      ");
  1485.       str += 1;
  1486.     }
  1487.     if (NULL != h_entry->val)
  1488.       fprintf (stderr, str, *(h_entry->val));
  1489.     else
  1490.       fprintf (stderr, str);
  1491.     fprintf (stderr, "\n");
  1492.     h_entry += 1;
  1493.     lines -= 1;
  1494.   }
  1495.   exit (FATAL);
  1496. }
  1497.  
  1498. #define NO_ERROR 0
  1499. #define NO_TERMINAL_DESCRIPTOR 2
  1500. #define Certainly_enough_space 2048    /* page 3, Section 1.1, para 4 */
  1501.  
  1502. /* Return the number of rows this terminal has. */
  1503. /* This is used only by help message, so we     */
  1504. /* will build termcap buffer on stack.          */
  1505. int
  1506. get_terminal_rows ()
  1507. {
  1508. #if defined (unix)
  1509.   int rows = 0;
  1510.   int result;
  1511.   char termcap_buffer[Certainly_enough_space];
  1512.   char temp_string_buffer[256];
  1513.   char *terminal_name = getenv ("TERM");
  1514.  
  1515.   if (terminal_name == NULL || *terminal_name == 0
  1516.       || (strcmp (terminal_name, "dialup") == 0))
  1517.     {
  1518.       terminal_name = temp_string_buffer;
  1519. #ifndef atarist
  1520.       printf ("\nTerminal Type:");
  1521.       fflush (stdout);
  1522.       fgets (terminal_name, 256, stdin);
  1523.       if (!(*terminal_name))
  1524.     return (NO_TERMINAL_DESCRIPTOR);
  1525. #else  /* atarist */
  1526.       *terminal_name = '\0';
  1527.       /* We have at least a default terminal descriptor on Atari */
  1528. #endif /* atarist */
  1529.     }
  1530. #if defined (unix)
  1531.   result = tgetent (termcap_buffer, terminal_name);
  1532. #else /* !unix */
  1533. #error "If your system really supports tgetent add '-Dunix' to your flags"
  1534. #endif /* !unix */
  1535.  
  1536. #if defined (TIOCGWINSZ)
  1537.   {
  1538.     int tty;
  1539.     struct winsize size;
  1540.  
  1541.     tty = fileno (stdin);
  1542.  
  1543.     if (ioctl (tty, TIOCGWINSZ, &size) != -1)
  1544.       rows = size.ws_row;
  1545.   }
  1546. #endif /* TIOCGWINSZ */
  1547.  
  1548.   if (!rows && NO_ERROR == result)
  1549.     rows = tgetnum ("li");
  1550.  
  1551.   if (rows <= 0)
  1552.     rows = 24;
  1553.  
  1554.   return (rows);
  1555. #else /* !unix */
  1556. #error "If you cannot find a better routine comment out this line"
  1557.   return (24);
  1558. #endif /* !unix */
  1559. }
  1560.  
  1561. /* **************************************************************** */
  1562. /*                                    */
  1563. /*            Manipulating Lists                  */
  1564. /*                                        */
  1565. /* **************************************************************** */
  1566.  
  1567. /** already defined
  1568. typedef struct generic_list {
  1569.   struct generic_list *next;
  1570. } GENERIC_LIST;
  1571. **/
  1572.  
  1573. /* Reverse the chain of structures in LIST.  Output the new head
  1574.    of the chain.  You should always assign the output value of this
  1575.    function to something, or you will lose the chain. */
  1576. GENERIC_LIST *
  1577. reverse_list (list)
  1578.      register GENERIC_LIST *list;
  1579. {
  1580.   register GENERIC_LIST *next;
  1581.   register GENERIC_LIST *prev = (GENERIC_LIST *) NULL;
  1582.  
  1583.   while (list)
  1584.     {
  1585.       next = list->next;
  1586.       list->next = prev;
  1587.       prev = list;
  1588.       list = next;
  1589.     }
  1590.   return (prev);
  1591. }
  1592.  
  1593.  
  1594. /* **************************************************************** */
  1595. /*                                    */
  1596. /*            Pushing and Popping Files               */
  1597. /*                                    */
  1598. /* **************************************************************** */
  1599.  
  1600. /* Find and load the file named FILENAME.  Return a pointer to
  1601.    the loaded file, or NULL if it can't be loaded. */
  1602. char *
  1603. find_and_load (filename)
  1604.      char *filename;
  1605. {
  1606.   struct stat fileinfo;
  1607.   int file = -1;
  1608.   size_t count = 0;
  1609. #if defined (VMS)
  1610.   int n;
  1611. #endif /* !VMS */
  1612.   char *fullpath, *result, *get_file_info_in_path ();
  1613.  
  1614.   result = fullpath = (char *)NULL;
  1615.  
  1616.   fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo);
  1617.  
  1618.   if (!fullpath)
  1619.     goto error_exit;
  1620.  
  1621.   filename = fullpath;
  1622.  
  1623.   file = open (filename, O_RDONLY);
  1624.   if (file < 0)
  1625.     goto error_exit;
  1626.  
  1627.   /* Load the file. */
  1628.   result = (char *)xmalloc (1 + fileinfo.st_size);
  1629.  
  1630.   /* VMS stat lies about the st_size value.  The actual number of
  1631.      readable bytes is always less than this value.  The arcane
  1632.      mysteries of VMS/RMS are too much to probe, so this hack
  1633.     suffices to make things work. */
  1634. #if defined (VMS)
  1635.   while ((n = read (file, result + count, fileinfo.st_size)) > 0)
  1636.     count += n;
  1637.   if (n == -1)
  1638. #else /* !VMS */
  1639.     count = fileinfo.st_size;
  1640. #ifndef atarist
  1641.     if (read (file, result, fileinfo.st_size) != fileinfo.st_size)
  1642. #else  /* atarist  - be careful about sizes!! ints are not good enough */
  1643.     if (_read (file, result, fileinfo.st_size) != fileinfo.st_size)
  1644. #endif /* atarist */
  1645. #endif /* !VMS */
  1646.   error_exit:
  1647.     {
  1648.       if (result)
  1649.     free (result);
  1650.  
  1651.       if (fullpath)
  1652.     free (fullpath);
  1653.  
  1654.       if (file != -1)
  1655.     close (file);
  1656.  
  1657.       return ((char *) NULL);
  1658.     }
  1659.   close (file);
  1660.  
  1661.   /* Set the globals to the new file. */
  1662.   input_text = result;
  1663. #ifndef atarist
  1664.   size_of_input_text = count;
  1665. #else  /* atarist */
  1666.   /*
  1667.    * We have to deal with possible CR/LF line ends.
  1668.    * We will toss all that garbage on input and restore it when
  1669.    * writing file out - see flush_output()
  1670.    */
  1671.   {
  1672.     char *text_pos = result, *text_end = result + count;
  1673.     while (text_pos < text_end) {
  1674.       if ('\r' != (*result = *text_pos++))
  1675.       result += 1;
  1676.     }
  1677.     size_of_input_text = result - input_text;
  1678.     /* return extra memory back to the system */
  1679.     input_text = xrealloc(input_text, size_of_input_text);
  1680.   }
  1681. #endif /* atarist */
  1682.   input_filename = savestring (fullpath);
  1683.   node_filename = savestring (fullpath);
  1684.   input_text_offset = 0;
  1685.   line_number = 1;
  1686.   /* Not strictly necessary.  This magic prevents read_token () from doing
  1687.      extra unnecessary work each time it is called (that is a lot of times).
  1688.      The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */
  1689.   input_text[size_of_input_text] = '\n';
  1690.   return (input_text);
  1691. }
  1692.  
  1693. /* Save the state of the current input file. */
  1694. void
  1695. pushfile ()
  1696. {
  1697.   FSTACK *newstack = (FSTACK *) xmalloc (sizeof (FSTACK));
  1698.   newstack->filename = input_filename;
  1699.   newstack->text = input_text;
  1700.   newstack->size = size_of_input_text;
  1701.   newstack->offset = input_text_offset;
  1702.   newstack->line_number = line_number;
  1703.   newstack->next = filestack;
  1704.  
  1705.   filestack = newstack;
  1706.   push_node_filename ();
  1707. }
  1708.  
  1709. /* Make the current file globals be what is on top of the file stack. */
  1710. void
  1711. popfile ()
  1712. {
  1713.   extern int executing_string;
  1714.   FSTACK *temp = filestack;
  1715.  
  1716.   if (!filestack)
  1717.     abort ();            /* My fault.  I wonder what I did? */
  1718.  
  1719.   /* Make sure that commands with braces have been satisfied. */
  1720.   if (!executing_string)
  1721.     discard_braces ();
  1722.  
  1723.   /* Get the top of the stack into the globals. */
  1724.   input_filename = filestack->filename;
  1725.   input_text = filestack->text;
  1726.   size_of_input_text = filestack->size;
  1727.   input_text_offset = filestack->offset;
  1728.   line_number = filestack->line_number;
  1729.  
  1730.   /* Pop the stack. */
  1731.   filestack = filestack->next;
  1732.   free (temp);
  1733.   pop_node_filename ();
  1734. }
  1735.  
  1736. /* Flush all open files on the file stack. */
  1737. void
  1738. flush_file_stack ()
  1739. {
  1740.   while (filestack)
  1741.     {
  1742.       free (input_filename);
  1743.       free (input_text);
  1744.       popfile ();
  1745.     }
  1746. }
  1747.  
  1748. int node_filename_stack_index = 0;
  1749. int node_filename_stack_size = 0;
  1750. char **node_filename_stack = (char **)NULL;
  1751.  
  1752. void
  1753. push_node_filename ()
  1754. {
  1755.   if (node_filename_stack_index + 1 > node_filename_stack_size)
  1756.     {
  1757.       if (!node_filename_stack)
  1758.     node_filename_stack =
  1759.       (char **)xmalloc ((node_filename_stack_size += 10)
  1760.                 * sizeof (char *));
  1761.       else
  1762.     node_filename_stack =
  1763.       (char **)xrealloc (node_filename_stack,
  1764.                  (node_filename_stack_size + 10)
  1765.                  * sizeof (char *));
  1766.     }
  1767.  
  1768.   node_filename_stack[node_filename_stack_index] = node_filename;
  1769.   node_filename_stack_index++;
  1770. }
  1771.  
  1772. void
  1773. pop_node_filename ()
  1774. {
  1775.   node_filename = node_filename_stack[--node_filename_stack_index];
  1776. }
  1777.  
  1778. /* Return just the simple part of the filename; i.e. the
  1779.    filename without the path information, or extensions.
  1780.    This conses up a new string. */
  1781. char *
  1782. filename_part (filename)
  1783.      char *filename;
  1784. {
  1785.   char *basename;
  1786.  
  1787.   basename = strrchr (filename, '/');
  1788.   if (!basename)
  1789.     basename = filename;
  1790.   else
  1791.     basename++;
  1792.  
  1793.   basename = savestring (basename);
  1794. #if defined (REMOVE_OUTPUT_EXTENSIONS)
  1795.  
  1796.   /* See if there is an extension to remove.  If so, remove it. */
  1797.   {
  1798.     char *temp;
  1799.  
  1800.     temp = strrchr (basename, '.');
  1801.     if (temp)
  1802.       *temp = '\0';
  1803.   }
  1804. #endif /* REMOVE_OUTPUT_EXTENSIONS */
  1805.   return (basename);
  1806. }
  1807.  
  1808. /* Return the pathname part of filename.  This can be NULL. */
  1809. char *
  1810. pathname_part (filename)
  1811.      char *filename;
  1812. {
  1813.   char *result = (char *) NULL;
  1814.   register size_t i;
  1815.  
  1816.   filename = expand_filename (filename, "");
  1817.  
  1818. #ifdef atarist
  1819.   (void) dos2unx(filename, filename);
  1820. #endif /* atarist */
  1821.   i = strlen (filename) - 1;
  1822.  
  1823.   while (i && filename[i] != '/')
  1824.     i--;
  1825.   if (filename[i] == '/')
  1826.     i++;
  1827.  
  1828.   if (i)
  1829.     {
  1830.       result = (char *)xmalloc (1 + i);
  1831.       strncpy (result, filename, i);
  1832.       result[i] = '\0';
  1833.     }
  1834.   free (filename);
  1835.   return (result);
  1836. }
  1837.  
  1838. /* Return the expansion of FILENAME. */
  1839. char *
  1840. expand_filename (filename, input_name)
  1841.      char *filename;
  1842.      const char *input_name;
  1843. {
  1844. #ifdef atarist
  1845.   (void) dos2unx(filename, filename);
  1846. #endif /* !atarist */
  1847.  
  1848.   filename = full_pathname (filename);
  1849.  
  1850.   if (filename[0] == '.')
  1851.     return (filename);
  1852.  
  1853.   if (filename[0] != '/' && input_name[0] == '/')
  1854.     {
  1855.       /* Make it so that relative names work. */
  1856.       char *result;
  1857.       int i = strlen (input_name) - 1;
  1858.  
  1859.       result = (char *)xmalloc (1 + strlen (input_name) + strlen (filename));
  1860.       strcpy (result, input_name);
  1861.  
  1862.       while (result[i] != '/' && i)
  1863.     i--;
  1864.  
  1865.       if (result[i] == '/')
  1866.     i++;
  1867.  
  1868.       strcpy (&result[i], filename);
  1869.       free (filename);
  1870.       return (result);
  1871.     }
  1872.   return (filename);
  1873. }
  1874.  
  1875. /* Return the full path to FILENAME. */
  1876. char *
  1877. full_pathname (filename)
  1878.      char *filename;
  1879. {
  1880.   int initial_character;
  1881.  
  1882.   if (filename && (initial_character = *filename))
  1883.     {
  1884.       if (initial_character == '/')
  1885.     return (savestring (filename));
  1886.       if (initial_character != '~')
  1887.     {
  1888.       return (savestring (filename));
  1889.     }
  1890.       else
  1891.     {
  1892.       if (filename[1] == '/')
  1893.         {
  1894.           /* Return the concatenation of HOME and the rest of the string. */
  1895.           char *temp_home;
  1896.           char *temp_name;
  1897.  
  1898.           temp_home = (char *) getenv ("HOME");
  1899.           temp_name = (char *)xmalloc (strlen (&filename[2])
  1900.                        + 1
  1901.                        + temp_home ? strlen (temp_home)
  1902.                        : 0);
  1903.           if (temp_home)
  1904.         strcpy (temp_name, temp_home);
  1905.  
  1906.           strcat (temp_name, &filename[2]);
  1907.           return (temp_name);
  1908.         }
  1909.       else
  1910.         {
  1911.           struct passwd *user_entry;
  1912.           int i, c;
  1913.           char *username = (char *)xmalloc ((size_t)257);
  1914.           char *temp_name;
  1915.  
  1916.           for (i = 1; (c = filename[i]); i++)
  1917.         {
  1918.           if (c == '/')
  1919.             break;
  1920.           else
  1921.             username[i - 1] = c;
  1922.         }
  1923.           if (c)
  1924.         username[i - 1] = '\0';
  1925.  
  1926.           user_entry = getpwnam (username);
  1927.  
  1928.           if (!user_entry)
  1929.         return (savestring (filename));
  1930.  
  1931.           temp_name = (char *)xmalloc (1 + strlen (user_entry->pw_dir)
  1932.                        + strlen (&filename[i]));
  1933.           strcpy (temp_name, user_entry->pw_dir);
  1934.           strcat (temp_name, &filename[i]);
  1935.           return (temp_name);
  1936.         }
  1937.     }
  1938.     }
  1939.   else
  1940.     {
  1941.       return (savestring (filename));
  1942.     }
  1943. }
  1944.  
  1945. /* **************************************************************** */
  1946. /*                                    */
  1947. /*            Error Handling                    */
  1948. /*                                    */
  1949. /* **************************************************************** */
  1950.  
  1951. /* Number of errors encountered. */
  1952. int errors_printed = 0;
  1953.  
  1954. /* Print the last error gotten from the file system. */
  1955. void
  1956. fs_error (filename)
  1957.      char *filename;
  1958. {
  1959.   remember_error ();
  1960.   perror (filename);
  1961.   /* return (0); */
  1962. }
  1963.  
  1964. /* Print an error message, and return false. */
  1965. #if __STDC__
  1966. /* Make error routines into someting suitable for a Standard compiler */
  1967.  
  1968. /* Print an error message - values are not ever used. */
  1969. void
  1970. error (const char *format, ...)
  1971. {
  1972.   va_list args;
  1973.  
  1974.   remember_error ();
  1975.   va_start(args, format);
  1976.   vfprintf (STDERR, format, args);
  1977.   va_end(args);
  1978.   fprintf (STDERR, "\n");
  1979. }
  1980.  
  1981. /* Just like error (), but print the line number as well. */
  1982. void
  1983. line_error (const char *format, ...)
  1984. {
  1985.   va_list args;
  1986.  
  1987.   remember_error ();
  1988.   fprintf (STDERR, "%s:%lu: ", input_filename, line_number);
  1989.   va_start(args, format);
  1990.   vfprintf (STDERR, format, args);
  1991.   va_end(args);
  1992.   fprintf (STDERR, ".\n");
  1993.   return;
  1994. }
  1995.  
  1996. void
  1997. warning (const char *format, ...)
  1998. {
  1999.   va_list args;
  2000.  
  2001.   if (print_warnings)
  2002.     {
  2003.       fprintf (STDERR, "%s:%lu: Warning: ", input_filename, line_number);
  2004.       va_start(args, format);
  2005.       vfprintf (STDERR, format, args);
  2006.       va_end(args);
  2007.       fprintf (STDERR, ".\n");
  2008.     }
  2009. }
  2010.  
  2011. #else /* !(__STDC__) */
  2012. #if defined (HAVE_VARARGS_H) && defined (HAVE_VFPRINTF)
  2013.  
  2014. int
  2015. error (va_alist)
  2016.      va_dcl
  2017. {
  2018.   char *format;
  2019.   va_list args;
  2020.  
  2021.   remember_error ();
  2022.   va_start (args);
  2023.   format = va_arg (args, char *);
  2024.   vfprintf (stderr, format, args);
  2025.   va_end (args);
  2026.   fprintf (stderr, "\n");
  2027. }
  2028.  
  2029. /* Just like error (), but print the line number as well. */
  2030. int
  2031. line_error (va_alist)
  2032.      va_dcl
  2033. {
  2034.   char *format;
  2035.   va_list args;
  2036.  
  2037.   remember_error ();
  2038.   va_start (args);
  2039.   format = va_arg (args, char *);
  2040.   fprintf (STDERR, "%s:%lu: ", input_filename, line_number);
  2041.   vfprintf (STDERR, format, args);
  2042.   fprintf (STDERR, ".\n");
  2043.   va_end (args);
  2044.   return ((int) 0);
  2045. }
  2046.  
  2047. int
  2048. warning (va_alist)
  2049.      va_dcl
  2050. {
  2051.   char *format;
  2052.   va_list args;
  2053.  
  2054.   va_start (args);
  2055.   format = va_arg (args, char *);
  2056.   if (print_warnings)
  2057.     {
  2058.       fprintf (STDERR, "%s:%lu: Warning: ", input_filename, line_number);
  2059.       vfprintf (STDERR, format, args);
  2060.       fprintf (STDERR, ".\n");
  2061.     }
  2062.   va_end (args);
  2063.   return ((int) 0);
  2064. }
  2065.  
  2066. #else /* !(HAVE_VARARGS_H && HAVE_VFPRINTF) */
  2067.  
  2068. int
  2069. error (format, arg1, arg2, arg3, arg4, arg5)
  2070.      char *format;
  2071. {
  2072.   remember_error ();
  2073.   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  2074.   fprintf (stderr, "\n");
  2075.   return ((int) 0);
  2076. }
  2077.  
  2078. /* Just like error (), but print the line number as well. */
  2079. int
  2080. line_error (format, arg1, arg2, arg3, arg4, arg5)
  2081.      char *format;
  2082. {
  2083.   remember_error ();
  2084.   fprintf (stderr, "%s:%lu: ", input_filename, line_number);
  2085.   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  2086.   fprintf (stderr, ".\n");
  2087.   return ((int) 0);
  2088. }
  2089.  
  2090. int
  2091. warning (format, arg1, arg2, arg3, arg4, arg5)
  2092.      char *format;
  2093. {
  2094.   if (print_warnings)
  2095.     {
  2096.       fprintf (stderr, "%s:%lu: Warning: ", input_filename, line_number);
  2097.       fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  2098.       fprintf (stderr, ".\n");
  2099.     }
  2100.   return ((int) 0);
  2101. }
  2102.  
  2103. #endif /* !(HAVE_VARARGS_H && HAVE_VFPRINTF) */
  2104. #endif /* __STDC__ */
  2105.  
  2106.  
  2107. /* Remember that an error has been printed.  If this is the first
  2108.    error printed, then tell them which program is printing them.
  2109.    If more than max_error_level have been printed, then exit the
  2110.    program. */
  2111. void
  2112. remember_error ()
  2113. {
  2114.   errors_printed++;
  2115.   if (max_error_level && (errors_printed > max_error_level))
  2116.     {
  2117.       fprintf (stderr, "Too many errors!  Gave up.\n");
  2118.       flush_file_stack ();
  2119.       cm_bye ();
  2120.       exit (1);
  2121.     }
  2122. }
  2123.  
  2124. /* **************************************************************** */
  2125. /*                                    */
  2126. /*            Hacking Tokens and Strings            */
  2127. /*                                    */
  2128. /* **************************************************************** */
  2129.  
  2130. /* Return the next token as a string pointer.  We cons the
  2131.    string. */
  2132. char *
  2133. read_token ()
  2134. {
  2135.   size_t i;
  2136.   int character;
  2137.   char *result;
  2138.  
  2139.   /* If the first character to be read is self-delimiting, then that
  2140.      is the command itself. */
  2141.   character = curchar ();
  2142.   if (self_delimiting (character))
  2143.     {
  2144.       input_text_offset++;
  2145.       result = savestring (" ");
  2146.       *result = character;
  2147.       return (result);
  2148.     }
  2149.  
  2150.   for (i = 0; ((input_text_offset != size_of_input_text)
  2151.            && (character = curchar ())
  2152.            && command_char (character));
  2153.        i++, input_text_offset++);
  2154.   result = (char *)xmalloc (i + 1);
  2155.   memcpy (result, &input_text[input_text_offset - i], i);
  2156.   result[i] = '\0';
  2157.   return (result);
  2158. }
  2159.  
  2160. /* Return non-zero if CHARACTER is self-delimiting. */
  2161. int
  2162. self_delimiting (character)
  2163.      int character;
  2164. {
  2165. #ifndef atarist
  2166.   return (member (character, "{}:.@*'`,!?; \n"));
  2167. #else  /* we do not want to see any CR's in our names */
  2168.   return (isspace(character) || member (character, "{}:.@*'`,!?;"));
  2169. #endif /* atarist */
  2170. }
  2171.  
  2172. /* Clear whitespace from the front and end of string. */
  2173. void
  2174. canon_white (string)
  2175.      char *string;
  2176. {
  2177.   int len = strlen (string);
  2178.   int x;
  2179.  
  2180.   if (!len)
  2181.     return;
  2182.  
  2183.   for (x = 0; x < len; x++)
  2184.     {
  2185.       if (!cr_or_whitespace (string[x]))
  2186.     {
  2187.       if(0 != x)
  2188.         (void)strcpy (string, string + x);
  2189.       break;
  2190.     }
  2191.     }
  2192.   len = strlen (string);
  2193.   if (len)
  2194.     len--;
  2195.   while (len > -1 && cr_or_whitespace (string[len]))
  2196.     len--;
  2197.   string[len + 1] = '\0';
  2198. }
  2199.  
  2200. /* Bash STRING, replacing all whitespace with just one space. */
  2201. void
  2202. fix_whitespace (string)
  2203.      char *string;
  2204. {
  2205.   char *temp = (char *)xmalloc (strlen (string) + 1);
  2206.   int string_index = 0;
  2207.   int temp_index = 0;
  2208.   int c;
  2209.  
  2210.   canon_white (string);
  2211.  
  2212.   while (string[string_index])
  2213.     {
  2214.       c = temp[temp_index++] = string[string_index++];
  2215.       /*
  2216.        * don't expand macro cr_or_whitespace - it may have
  2217.        * different meanings on different operating systems
  2218.        */
  2219.       if (cr_or_whitespace(c))
  2220.     {
  2221.       temp[temp_index - 1] = ' ';
  2222.       while ((c = string[string_index]) && cr_or_whitespace(c))
  2223.         string_index++;
  2224.     }
  2225.     }
  2226.   temp[temp_index] = '\0';
  2227.   strcpy (string, temp);
  2228.   free (temp);
  2229. }
  2230.  
  2231. /* Discard text until the desired string is found.  The string is
  2232.    included in the discarded text. */
  2233. void
  2234. discard_until (string)
  2235.      const char *string;
  2236. {
  2237.   size_t temp = search_forward (string, input_text_offset);
  2238.  
  2239.   size_t tt = ((long)temp < 0) ? size_of_input_text : temp + strlen (string);
  2240.   size_t from = input_text_offset;
  2241.  
  2242.   /* Find out what line we are on. */
  2243.   while (from != tt)
  2244.     if (input_text[from++] == '\n')
  2245.       line_number++;
  2246.  
  2247.   if ((long)temp < 0)
  2248.     {
  2249.       input_text_offset = size_of_input_text - strlen (string);
  2250.  
  2251.       if (strcmp (string, "\n") != 0)
  2252.     {
  2253.       line_error ("Expected `%s'", string);
  2254.       return;
  2255.     }
  2256.     }
  2257.   else
  2258.     input_text_offset = temp;
  2259.  
  2260.   input_text_offset += strlen (string);
  2261. }
  2262.  
  2263. /* Read characters from the file until we are at MATCH.
  2264.    Place the characters read into STRING.
  2265.    On exit input_text_offset is after the match string.
  2266.    Return the offset where the string starts. */
  2267. size_t
  2268. get_until (match, string)
  2269.      const char *match;
  2270.      char **string;
  2271. {
  2272.   size_t len, current_point, x, new_point, tem;
  2273.  
  2274.   current_point = x = input_text_offset;
  2275.   new_point = search_forward (match, input_text_offset);
  2276.  
  2277.   if ((long)new_point < 0)
  2278.     new_point = size_of_input_text;
  2279.   len = new_point - current_point;
  2280.  
  2281.   /* Keep track of which line number we are at. */
  2282.   tem = new_point + (strlen (match) - 1);
  2283.   while (x != tem)
  2284.     if (input_text[x++] == '\n')
  2285.       line_number++;
  2286.  
  2287.   *string = (char *)xmalloc (len + 1);
  2288.  
  2289.   memcpy (*string, &input_text[current_point], len);
  2290.   (*string)[len] = '\0';
  2291.  
  2292.   /* Now leave input_text_offset in a consistent state. */
  2293.   input_text_offset = tem;
  2294.  
  2295.   if (input_text_offset > size_of_input_text)
  2296.     input_text_offset = size_of_input_text;
  2297.  
  2298.   return (new_point);
  2299. }
  2300.  
  2301. /* Read characters from the file until we are at MATCH or end of line.
  2302.    Place the characters read into STRING.  */
  2303. void
  2304. get_until_in_line (match, string)
  2305.      const char *match;
  2306.      char **string;
  2307. {
  2308.   size_t real_bottom, temp;
  2309.  
  2310.   real_bottom = size_of_input_text;
  2311.   temp = search_forward ("\n", input_text_offset);
  2312.  
  2313.   if ((long)temp < 0)
  2314.     temp = size_of_input_text;
  2315.  
  2316.   size_of_input_text = temp;
  2317.   get_until (match, string);
  2318.   size_of_input_text = real_bottom;
  2319. }
  2320.  
  2321. void
  2322. get_rest_of_line (string)
  2323.      char **string;
  2324. {
  2325.   get_until ("\n", string);
  2326.   canon_white (*string);
  2327.  
  2328.   if (curchar () == '\n')    /* as opposed to the end of the file... */
  2329.     {
  2330.       line_number++;
  2331.       input_text_offset++;
  2332.     }
  2333. }
  2334.  
  2335. /* Backup the input pointer to the previous character, keeping track
  2336.    of the current line number. */
  2337. void
  2338. backup_input_pointer ()
  2339. {
  2340.   if (input_text_offset)
  2341.     {
  2342.       input_text_offset--;
  2343.       if (curchar () == '\n')
  2344.     line_number--;
  2345.     }
  2346. }
  2347.  
  2348. /* Read characters from the file until we are at MATCH or closing brace.
  2349.    Place the characters read into STRING.  */
  2350. void
  2351. get_until_in_braces (match, string)
  2352.      const char *match;
  2353.      char **string;
  2354. {
  2355.   size_t i, brace = 0;
  2356.   size_t match_len = strlen (match);
  2357.   char *temp;
  2358.  
  2359.   for (i = input_text_offset; i < size_of_input_text; i++)
  2360.     {
  2361.       if (input_text[i] == '{')
  2362.     brace++;
  2363.       else if (input_text[i] == '}')
  2364.     brace--;
  2365.       else if (input_text[i] == '\n')
  2366.     line_number++;
  2367.  
  2368.       if ((long) brace < 0 ||
  2369.       (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
  2370.     break;
  2371.     }
  2372.  
  2373.   match_len = i - input_text_offset;
  2374.   temp = (char *)xmalloc (2 + match_len);
  2375.   strncpy (temp, input_text + input_text_offset, match_len);
  2376.   temp[match_len] = '\0';
  2377.   input_text_offset = i;
  2378.   *string = temp;
  2379. }
  2380.  
  2381. /* **************************************************************** */
  2382. /*                                    */
  2383. /*            Converting the File                 */
  2384. /*                                    */
  2385. /* **************************************************************** */
  2386.  
  2387. /* Convert the file named by NAME.  The output is saved on the file
  2388.    named as the argument to the @setfilename command. */
  2389. static const char *suffixes[] = {
  2390.   "",
  2391.   ".texinfo",
  2392.   ".texi",
  2393.   ".txinfo",
  2394.   ".txi",        /* an allowance for 8+3 type file names */
  2395.   (const char *)NULL
  2396. };
  2397.  
  2398. void
  2399. convert (name)
  2400.      char *name;
  2401. {
  2402.   char *real_output_filename; /* , *expand_filename (), *filename_part (); */
  2403.   char *filename;
  2404.   register int i;
  2405.  
  2406.   init_tag_table ();
  2407.   init_internals ();
  2408.   if (only_split) {
  2409.     split_file (name, (size_t)0);
  2410.     return;
  2411.   }
  2412.   init_paragraph ();
  2413.   init_indices ();
  2414.  
  2415.   /* Try to load the file specified by NAME.  If the file isn't found, and
  2416.      there is no suffix in NAME, then try NAME.texinfo, and NAME.texi. */
  2417.   filename = (char *)xmalloc (strlen (name) + 50);
  2418.   for (i = 0; suffixes[i]; i++)
  2419.     {
  2420.       strcpy (filename, name);
  2421.       strcat (filename, suffixes[i]);
  2422.  
  2423.       if (find_and_load (filename))
  2424.     break;
  2425.  
  2426.       if (!suffixes[i][0] && strrchr (filename, '.'))
  2427.     {
  2428.       fs_error (filename);
  2429.       free (filename);
  2430.       return;
  2431.     }
  2432.     }
  2433.  
  2434.   if (!suffixes[i])
  2435.     {
  2436.       fs_error (name);
  2437.       free (filename);
  2438.       return;
  2439.     }
  2440.  
  2441.   input_filename = filename;
  2442.  
  2443.   /* Search this file looking for the special string which starts conversion.
  2444.      Once found, we may truly begin. */
  2445.  
  2446.   input_text_offset = search_forward ("@setfilename", (size_t)0);
  2447.  
  2448.   if ((long)input_text_offset < 0)
  2449.     {
  2450.       if (!command_output_filename)
  2451.     {
  2452.       error ("No `@setfilename' found in `%s'", name);
  2453.       goto finished;
  2454.     }
  2455.     }
  2456.   else
  2457.     input_text_offset += strlen ("@setfilename");
  2458.  
  2459.   real_output_filename = (char *)NULL;
  2460.  
  2461.   if (!command_output_filename)
  2462.     get_until ("\n", &output_filename);
  2463.   else
  2464.     {
  2465.       if (input_text_offset != -1)
  2466.     discard_until ("\n");
  2467.       else
  2468.     input_text_offset = 0;
  2469.  
  2470.       real_output_filename = output_filename = command_output_filename;
  2471.       command_output_filename = (char *)NULL;
  2472.     }
  2473.  
  2474.   canon_white (output_filename);
  2475.   printf ("Making info file `%s' from `%s'.\n", output_filename, name);
  2476.  
  2477.   if (verbose_mode)
  2478.     fprintf (stderr, "  The input file contains %lu characters.\n",
  2479.          size_of_input_text);
  2480.  
  2481.   if (real_output_filename &&
  2482.       strcmp (real_output_filename, "-") == 0)
  2483.     {
  2484.       output_stream = stdout;
  2485.     }
  2486.   else
  2487.     {
  2488.       if (!real_output_filename)
  2489.     real_output_filename = expand_filename (output_filename, name);
  2490.  
  2491.       output_stream = fopen (real_output_filename, WMODE);
  2492.     }
  2493.  
  2494.   if (output_stream == NULL)
  2495.     {
  2496.       fs_error (real_output_filename);
  2497.       goto finished;
  2498.     }
  2499.  
  2500.   /* Make the displayable filename from output_filename.  Only the base
  2501.      portion of the filename need be displayed. */
  2502.   pretty_output_filename = filename_part (output_filename);
  2503.  
  2504.   /* For this file only, count the number of newlines from the top of
  2505.      the file to here.  This way, we keep track of line numbers for
  2506.      error reporting.  Line_number starts at 1, since the user isn't
  2507.      zero-based. */
  2508.   {
  2509.     int temp = 0;
  2510.     line_number = 1;
  2511.     while (temp != input_text_offset)
  2512.       if (input_text[temp++] == '\n')
  2513.     line_number++;
  2514.   }
  2515.  
  2516.   if (!no_headers)
  2517.     {
  2518.       add_word_args ("This is Info file %s, produced by Makeinfo-%d.%d from ",
  2519.              output_filename, major_version, minor_version);
  2520.       add_word_args ("the input file %s.\n", input_filename);
  2521.     }
  2522.  
  2523.   close_paragraph ();
  2524.   reader_loop ();
  2525.  
  2526. finished:
  2527.   close_paragraph ();
  2528.   flush_file_stack ();
  2529.   if (output_stream != NULL)
  2530.     {
  2531.       output_pending_notes ();
  2532.       free_pending_notes ();
  2533.       if (tag_table != NULL)
  2534.     {
  2535.       tag_table = (TAG_ENTRY *) reverse_list ((GENERIC_LIST *)tag_table);
  2536.       if (!no_headers)
  2537.         write_tag_table ();
  2538.     }
  2539.  
  2540.       if (output_stream != stdout)
  2541.     fclose (output_stream);
  2542.  
  2543.       /* If validating, then validate the entire file right now. */
  2544.       if (validating)
  2545.     validate_file (real_output_filename, tag_table);
  2546.  
  2547.       /* With a big file and a computer without virtual memory
  2548.      we may have not enough of contiguous memory
  2549.      to reload created info file back (it may grow on write out)
  2550.      so free what it is possible to free and restart */
  2551.  
  2552.       init_tag_table ();
  2553.       for (i = 0; i < defined_indices; i++) {
  2554. #ifndef NeXT
  2555.     /* Bug alert!! - cannot do that on NeXT since init_tag_table()
  2556.        and free_index() clobber memory for each other.
  2557.        I do not know why -- mj */
  2558.     free_index(the_indices[i]);
  2559. #endif /* NeXT */
  2560.     free (name_index_alist[i]->name);
  2561.     free (name_index_alist[i]);
  2562.       }
  2563.       free_and_clear ((char **)&the_indices);
  2564.       free_and_clear ((char **)&name_index_alist);
  2565.       free_and_clear ((char **)&output_paragraph);
  2566.       free_and_clear ((char **)¤t_node);
  2567.       free_and_clear (&command);
  2568.       free_and_clear (&input_filename);
  2569.  
  2570.       /* This used to test  && !errors_printed.
  2571.      But some files might have legit warnings.  So split anyway.  */
  2572.       if (splitting)
  2573.     split_file (real_output_filename, 0);
  2574.  
  2575.       free_and_clear (&real_output_filename);
  2576.       free_and_clear (&output_filename);
  2577.     }
  2578. }
  2579.  
  2580. void
  2581. free_and_clear (pointer)
  2582.      char **pointer;
  2583. {
  2584.   if ((*pointer) != (char *) NULL)
  2585.     {
  2586.       free (*pointer);
  2587.       *pointer = (char *) NULL;
  2588.     }
  2589. }
  2590.  
  2591.  /* Initialize some state. */
  2592. void
  2593. init_internals ()
  2594. {
  2595.   free_and_clear ((char **)¤t_node);
  2596.   free_and_clear (&output_filename);
  2597.   free_and_clear (&command);
  2598.   free_and_clear (&input_filename);
  2599.   free_node_references ();
  2600.   init_insertion_stack ();
  2601.   init_brace_stack ();
  2602.   command_index = 0;
  2603.   in_menu = 0;
  2604. }
  2605.  
  2606. void
  2607. init_paragraph ()
  2608. {
  2609.   free_and_clear ((char **)&output_paragraph);
  2610.   output_paragraph = (unsigned char *)xmalloc (paragraph_buffer_len);
  2611.   init_par_parameters ();
  2612. }
  2613.  
  2614. void
  2615. init_par_parameters ()
  2616. {
  2617.   output_position = 0;
  2618.   output_paragraph[0] = '\0';
  2619.   output_paragraph_offset = 0;
  2620.   output_column = 0;
  2621.   paragraph_is_open = 0;
  2622.   current_indent = 0;
  2623. }
  2624.  
  2625. /* Okay, we are ready to start the conversion.  Call the reader on
  2626.    some text, and fill the text as it is output.  Handle commands by
  2627.    remembering things like open braces and the current file position on a
  2628.    stack, and when the corresponding close brace is found, you can call
  2629.    the function with the proper arguments. */
  2630. void
  2631. reader_loop ()
  2632. {
  2633.   int character;
  2634.   int done = 0;
  2635.   int dash_count = 0;
  2636.  
  2637.   while (!done)
  2638.     {
  2639.       if (input_text_offset >= size_of_input_text)
  2640.     {
  2641.       if (filestack)
  2642.         {
  2643.           free (input_filename);
  2644.           free (input_text);
  2645.           popfile ();
  2646.         }
  2647.       else
  2648.         break;
  2649.     }
  2650.  
  2651.       character = curchar ();
  2652.  
  2653.       if (!in_fixed_width_font &&
  2654.       (character == '\'' || character == '`') &&
  2655.       input_text[input_text_offset + 1] == character)
  2656.     {
  2657.       input_text_offset++;
  2658.       character = '"';
  2659.     }
  2660.  
  2661.       if (character == '-')
  2662.     {
  2663.       dash_count++;
  2664.       if (dash_count == 2 && !in_fixed_width_font)
  2665.         {
  2666.           input_text_offset++;
  2667.           continue;
  2668.         }
  2669.     }
  2670.       else
  2671.     {
  2672.       dash_count = 0;
  2673.     }
  2674.  
  2675.       /* If this is a whitespace character, then check to see if the line
  2676.      is blank.  If so, advance to the carriage return. */
  2677.       if (whitespace (character))
  2678.     {
  2679.       register size_t i = input_text_offset + 1;
  2680.  
  2681.       while (i < size_of_input_text && whitespace (input_text[i]))
  2682.         i++;
  2683.  
  2684.       if (i == size_of_input_text || input_text[i] == '\n')
  2685.         {
  2686.           if (i == size_of_input_text)
  2687.         i--;
  2688.  
  2689.           input_text_offset = i;
  2690.           character = curchar ();
  2691.         }
  2692.     }
  2693.  
  2694.       if (character == '\n')
  2695.     {
  2696.       line_number++;
  2697. #ifdef DOTS
  2698.       /*
  2699.        * provide some indicator that we are still alive on slower machine
  2700.        */
  2701.           if (0 == (line_number & 0x3fL))
  2702.               fprintf(stderr, ".");
  2703. #endif
  2704.  
  2705.       /* Check for a menu entry here, since the "escape sequence"
  2706.          that begins menu entrys is "\n* ". */
  2707.       if (in_menu && input_text_offset + 1 < size_of_input_text)
  2708.         {
  2709.           char /* *glean_node_from_menu (), */ *tem;
  2710.  
  2711.           /* Note that the value of TEM is discarded, since it is
  2712.          gauranteed to be NULL when glean_node_from_menu () is
  2713.          called with a non-zero argument. */
  2714.           tem = glean_node_from_menu (1);
  2715.         }
  2716.     }
  2717.  
  2718.       switch (character)
  2719.     {
  2720.     case COMMAND_PREFIX:
  2721.       read_command ();
  2722.       if (strcmp (command, "bye") == 0)
  2723.         {
  2724.           done = 1;
  2725.           continue;
  2726.         }
  2727.       break;
  2728.  
  2729.     case '{':
  2730.  
  2731.       /* Special case.  I'm not supposed to see this character by itself.
  2732.          If I do, it means there is a syntax error in the input text.
  2733.          Report the error here, but remember this brace on the stack so
  2734.          you can ignore its partner. */
  2735.  
  2736.       line_error ("Misplaced `{'");
  2737.       remember_brace ((FUNCTION *)misplaced_brace);
  2738.  
  2739.       /* Don't advance input_text_offset since this happens in
  2740.          remember_brace ().
  2741.          input_text_offset++;
  2742.            */
  2743.       break;
  2744.  
  2745.     case '}':
  2746.       pop_and_call_brace ();
  2747.       input_text_offset++;
  2748.       break;
  2749.  
  2750.     default:
  2751.       add_char (character);
  2752.       input_text_offset++;
  2753.     }
  2754.     }
  2755. }
  2756.  
  2757. /* Find the command corresponding to STRING.  If the command
  2758.    is found, return a pointer to the data structure.  Otherwise
  2759.    return (-1). */
  2760. COMMAND *
  2761. get_command_entry (string)
  2762.      char *string;
  2763. {
  2764.   register int i;
  2765.  
  2766.   for (i = 0; CommandTable[i].name; i++)
  2767.     if (strcmp (CommandTable[i].name, string) == 0)
  2768.       return (&CommandTable[i]);
  2769.  
  2770.   /* This command is not in our predefined command table.  Perhaps
  2771.      it is a user defined command. */
  2772.   for (i = 0; i < user_command_array_len; i++)
  2773.     if (user_command_array[i] &&
  2774.     (strcmp (user_command_array[i]->name, string) == 0))
  2775.       return (user_command_array[i]);
  2776.  
  2777.   /* Nope, we never heard of this command. */
  2778.   return ((COMMAND *) -1L);
  2779. }
  2780.  
  2781. /* input_text_offset is right at the command prefix character.
  2782.    Read the next token to determine what to do. */
  2783. void
  2784. read_command ()
  2785. {
  2786.   COMMAND *entry;
  2787.   input_text_offset++;
  2788.   free_and_clear (&command);
  2789.   command = read_token ();
  2790.  
  2791. #if defined (HAVE_MACROS)
  2792.   /* Check to see if this command is a macro.  If so, execute it here. */
  2793.   {
  2794.     MACRO_DEF *def;
  2795.  
  2796.     def = find_macro (command);
  2797.  
  2798.     if (def)
  2799.       {
  2800.     execute_macro (def);
  2801.     return;
  2802.       }
  2803.   }
  2804. #endif /* HAVE_MACROS */
  2805.  
  2806.   entry = get_command_entry (command);
  2807.  
  2808.   if ((long) entry == -1L)
  2809.     {
  2810.       line_error ("Unknown info command `%s'", command);
  2811.       return;
  2812.     }
  2813.  
  2814.   if (entry->argument_in_braces)
  2815.     remember_brace (entry->proc);
  2816.  
  2817.   (*(entry->proc)) (START);
  2818. }
  2819.  
  2820. /* Return the string which invokes PROC; a pointer to a function. */
  2821. const char *
  2822. find_proc_name (proc)
  2823.      FUNCTION *proc;
  2824. {
  2825.   register int i;
  2826.  
  2827.   for (i = 0; CommandTable[i].name; i++)
  2828.     if (proc == CommandTable[i].proc)
  2829.       return (CommandTable[i].name);
  2830.   return ("NO_NAME!");
  2831. }
  2832.  
  2833. void
  2834. init_brace_stack ()
  2835. {
  2836.   brace_stack = (BRACE_ELEMENT *) NULL;
  2837. }
  2838.  
  2839. void
  2840. remember_brace (proc)
  2841.      FUNCTION *proc;
  2842. {
  2843.   if (curchar () != '{')
  2844.     line_error ("@%s expected `{..}'", command);
  2845.   else
  2846.     input_text_offset++;
  2847.   remember_brace_1 (proc, output_paragraph_offset);
  2848. }
  2849.  
  2850. /* Remember the current output position here.  Save PROC
  2851.    along with it so you can call it later. */
  2852. void
  2853. remember_brace_1 (proc, position)
  2854.      FUNCTION *proc;
  2855.      size_t position;
  2856. {
  2857.   BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
  2858.   new->next = brace_stack;
  2859.   new->proc = proc;
  2860.   new->pos = position;
  2861.   new->line = line_number;
  2862.   brace_stack = new;
  2863. }
  2864.  
  2865. /* Pop the top of the brace stack, and call the associated function
  2866.    with the args END and POS. */
  2867.  
  2868. void
  2869. pop_and_call_brace ()
  2870. {
  2871.   BRACE_ELEMENT *temp;
  2872.   FUNCTION *proc;
  2873.   size_t pos;
  2874.  
  2875.   if (brace_stack == (BRACE_ELEMENT *) NULL) {
  2876.     /* return (line_error ("Unmatched close brace")); */
  2877.       line_error ("Unmatched close brace");
  2878.   }
  2879.  
  2880.   pos = brace_stack->pos;
  2881.   proc = brace_stack->proc;
  2882.   temp = brace_stack->next;
  2883.   free (brace_stack);
  2884.   brace_stack = temp;
  2885.  
  2886.   /* return ((*proc) (END, pos, output_paragraph_offset)); */
  2887.   (*proc) (END, pos, output_paragraph_offset);
  2888. }
  2889.  
  2890. /* You call discard_braces () when you shouldn't have any braces on the stack.
  2891.    I used to think that this happens for commands that don't take arguments
  2892.    in braces, but that was wrong because of things like @code{foo @@}.  So now
  2893.    I only detect it at the beginning of nodes. */
  2894. void
  2895. discard_braces ()
  2896. {
  2897.   size_t temp_line_number = line_number;
  2898.   const char *proc_name;
  2899.  
  2900.   if (!brace_stack)
  2901.     return;
  2902.  
  2903.   while (brace_stack)
  2904.     {
  2905.       line_number = brace_stack->line;
  2906.       proc_name = find_proc_name (brace_stack->proc);
  2907.       line_error ("@%s missing close brace", proc_name);
  2908.       line_number = temp_line_number;
  2909.       pop_and_call_brace ();
  2910.     }
  2911. }
  2912.  
  2913. int
  2914. get_char_len (character)
  2915.      unsigned int character;
  2916. {
  2917.   /* Return the printed length of the character. */
  2918.   int len;
  2919.  
  2920.   switch (character)
  2921.     {
  2922.     case '\t':
  2923.       len = (output_column + 8) & 0xf7;
  2924.       if (len > fill_column)
  2925.     len = fill_column - output_column;
  2926.       else
  2927.     len = len - output_column;
  2928.       break;
  2929.  
  2930.     case '\n':
  2931.       len = fill_column - output_column;
  2932.       break;
  2933.  
  2934.     default:
  2935.       if (character < ' ')
  2936.     len = 2;
  2937.       else
  2938.     len = 1;
  2939.     }
  2940.   return (len);
  2941. }
  2942.  
  2943. #if __STDC__
  2944. void
  2945. add_word_args (const char *format, ...)
  2946. {
  2947.   va_list args;
  2948.   char buffer[1000];
  2949.  
  2950.   va_start(args, format);
  2951.   vsprintf (buffer, format, args);
  2952.   va_end(args);
  2953.   add_word (buffer);
  2954. }
  2955. #else /* !(__STDC__) */
  2956.  
  2957. #if defined (HAVE_VARARGS_H) && defined (HAVE_VSPRINTF)
  2958.  
  2959. void
  2960. add_word_args (va_alist)
  2961.      va_dcl
  2962. {
  2963.   char buffer[1000];
  2964.   char *format;
  2965.   va_list args;
  2966.  
  2967.   va_start (args);
  2968.   format = va_arg (args, char *);
  2969.   vsprintf (buffer, format, args);
  2970.   va_end (args);
  2971.   add_word (buffer);
  2972. }
  2973.  
  2974. #else /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
  2975.  
  2976. void
  2977. add_word_args (format, arg1, arg2, arg3, arg4, arg5)
  2978.      char *format;
  2979. {
  2980.   char buffer[1000];
  2981.   sprintf (buffer, format, arg1, arg2, arg3, arg4, arg5);
  2982.   add_word (buffer);
  2983. }
  2984.  
  2985. #endif /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
  2986. #endif /* __STDC__ */
  2987.  
  2988. /* Add STRING to output_paragraph. */
  2989. void
  2990. add_word (string)
  2991.      const char *string;
  2992. {
  2993.   while (*string)
  2994.     add_char (*string++);
  2995. }
  2996.  
  2997. /* Non-zero if the last character inserted has the syntax class of NEWLINE. */
  2998. int last_char_was_newline = 1;
  2999.  
  3000. /* The actual last inserted character.  Note that this may be something
  3001.    other than NEWLINE even if last_char_was_newline is 1. */
  3002. int last_inserted_character = 0;
  3003.  
  3004. /* Non-zero means that a newline character has already been
  3005.    inserted, so close_paragraph () should insert one less. */
  3006. int line_already_broken = 0;
  3007.  
  3008. /* When non-zero we have finished an insertion (see end_insertion ()) and we
  3009.    want to ignore false continued paragraph closings. */
  3010. int insertion_paragraph_closed = 0;
  3011.  
  3012. /* Add the character to the current paragraph.  If filling_enabled is
  3013.    non-zero, then do filling as well. */
  3014. void
  3015. add_char (character)
  3016.      unsigned int character;
  3017. {
  3018.   /* If we are avoiding outputting headers, and we are currently
  3019.      in a menu, then simply return. */
  3020.   if (no_headers && in_menu)
  3021.     return;
  3022.  
  3023.   /* If we are adding a character now, then we don't have to
  3024.      ignore close_paragraph () calls any more. */
  3025.   if (must_start_paragraph && character != '\n')
  3026.     {
  3027.       must_start_paragraph = 0;
  3028.       line_already_broken = 0;    /* The line is no longer broken. */
  3029.       if (current_indent > output_column)
  3030.     {
  3031.       indent (current_indent - output_column);
  3032.       output_column = current_indent;
  3033.     }
  3034.     }
  3035.  
  3036.   if (non_splitting_words && member (character, " \t\n"))
  3037.     character = ' ' | 0x80;
  3038.  
  3039.   insertion_paragraph_closed = 0;
  3040.  
  3041.   switch (character)
  3042.     {
  3043.     case '\n':
  3044.       if (!filling_enabled)
  3045.     {
  3046.       insert ('\n');
  3047.  
  3048.       if (force_flush_right)
  3049.         {
  3050.           close_paragraph ();
  3051.           /* Hack to force single blank lines out in this mode. */
  3052.           flush_output ();
  3053.         }
  3054.  
  3055.       output_column = 0;
  3056.  
  3057.       if (!no_indent && paragraph_is_open)
  3058.         indent (output_column = current_indent);
  3059.       break;
  3060.     }
  3061.       else /* CHARACTER is newline, and filling is enabled. */
  3062.     {
  3063.       if (sentence_ender (last_inserted_character))
  3064.         {
  3065.           insert (' ');
  3066.           output_column++;
  3067.           last_inserted_character = character;
  3068.         }
  3069.     }
  3070.  
  3071.       if (last_char_was_newline)
  3072.     {
  3073.       close_paragraph ();
  3074.       pending_indent = 0;
  3075.     }
  3076.       else
  3077.     {
  3078.       last_char_was_newline = 1;
  3079.       insert (' ');
  3080.       output_column++;
  3081.     }
  3082.       break;
  3083.  
  3084.     default:
  3085.       {
  3086.     int len = get_char_len (character);
  3087.     int suppress_insert = 0;
  3088.  
  3089.     if ((character == ' ') && (last_char_was_newline))
  3090.       {
  3091.         if (!paragraph_is_open)
  3092.           {
  3093.         pending_indent++;
  3094.         return;
  3095.           }
  3096.       }
  3097.  
  3098.     if (!paragraph_is_open)
  3099.       {
  3100.         start_paragraph ();
  3101.  
  3102.         /* If the paragraph is supposed to be indented a certain way,
  3103.            then discard all of the pending whitespace.  Otherwise, we
  3104.            let the whitespace stay. */
  3105.         if (!paragraph_start_indent)
  3106.           indent (pending_indent);
  3107.         pending_indent = 0;
  3108.       }
  3109.  
  3110.     if ((output_column += len) > fill_column)
  3111.       {
  3112.         if (filling_enabled)
  3113.           {
  3114.         size_t temp = output_paragraph_offset;
  3115.         while (--temp > 0 && output_paragraph[temp] != '\n')
  3116.           {
  3117.             /* If we have found a space, we have the place to break
  3118.                the line. */
  3119.             if (output_paragraph[temp] == ' ')
  3120.               {
  3121.             /* Remove trailing whitespace from output. */
  3122.             while (temp && whitespace (output_paragraph[temp - 1]))
  3123.               temp--;
  3124.  
  3125.             output_paragraph[temp++] = '\n';
  3126.  
  3127.             /* We have correctly broken the line where we want
  3128.                to.  What we don't want is spaces following where
  3129.                we have decided to break the line.  We get rid of
  3130.                them. */
  3131.             {
  3132.               size_t t1 = temp;
  3133.  
  3134.               for (;; t1++)
  3135.                 {
  3136.                   if (t1 == output_paragraph_offset)
  3137.                 {
  3138.                   if (whitespace (character))
  3139.                     suppress_insert = 1;
  3140.                   break;
  3141.                 }
  3142.                   if (!whitespace (output_paragraph[t1]))
  3143.                 break;
  3144.                 }
  3145.  
  3146.               if (t1 != temp)
  3147.                 {
  3148.                   strncpy ((char *) &output_paragraph[temp],
  3149.                        (char *) &output_paragraph[t1],
  3150.                        (output_paragraph_offset - t1));
  3151.                   output_paragraph_offset -= (t1 - temp);
  3152.                 }
  3153.             }
  3154.  
  3155.             /* Filled, but now indent if that is right. */
  3156.             if (indented_fill && current_indent)
  3157.               {
  3158.                 size_t buffer_len =
  3159.                 ((output_paragraph_offset - temp)
  3160.                           + current_indent);
  3161.                 char *temp_buffer = (char *)xmalloc (buffer_len);
  3162.                 int indentation = 0;
  3163.  
  3164.                 /* We have to shift any markers that are in
  3165.                    front of the wrap point. */
  3166.                 {
  3167.                   register BRACE_ELEMENT *stack = brace_stack;
  3168.  
  3169.                   while (stack)
  3170.                 {
  3171.                   if (stack->pos >= temp)
  3172.                     stack->pos += current_indent;
  3173.                   stack = stack->next;
  3174.                 }
  3175.                 }
  3176.  
  3177.                 while (current_indent > 0 &&
  3178.                    indentation != current_indent)
  3179.                   temp_buffer[indentation++] = ' ';
  3180.  
  3181.                 strncpy ((char *) &temp_buffer[current_indent],
  3182.                      (char *) &output_paragraph[temp],
  3183.                      buffer_len - current_indent);
  3184.  
  3185.                 if (output_paragraph_offset + buffer_len
  3186.                 >= paragraph_buffer_len)
  3187.                   {
  3188.                 unsigned char *tt = xrealloc
  3189.                   (output_paragraph,
  3190.                    (paragraph_buffer_len += buffer_len));
  3191.                 output_paragraph = tt;
  3192.                   }
  3193.                 strncpy ((char *) &output_paragraph[temp],
  3194.                      temp_buffer, buffer_len);
  3195.                 output_paragraph_offset += current_indent;
  3196.                 free (temp_buffer);
  3197.               }
  3198.             output_column = 0;
  3199.             while (temp < output_paragraph_offset)
  3200.               output_column +=
  3201.                 get_char_len (output_paragraph[temp++]);
  3202.             output_column += len;
  3203.             break;
  3204.               }
  3205.           }
  3206.           }
  3207.       }
  3208.  
  3209.     if (!suppress_insert)
  3210.       {
  3211.         insert (character);
  3212.         last_inserted_character = character;
  3213.       }
  3214.     last_char_was_newline = 0;
  3215.     line_already_broken = 0;
  3216.       }
  3217.     }
  3218. }
  3219.  
  3220. /* Insert CHARACTER into OUTPUT_PARAGRAPH. */
  3221. void
  3222. insert (character)
  3223.      unsigned int character;
  3224. {
  3225.   output_paragraph[output_paragraph_offset++] = character;
  3226.   if (output_paragraph_offset == paragraph_buffer_len)
  3227.     {
  3228.       output_paragraph =
  3229.     xrealloc (output_paragraph, (paragraph_buffer_len += 100));
  3230.     }
  3231. }
  3232.  
  3233. /* Remove upto COUNT characters of whitespace from the
  3234.    the current output line.  If COUNT is less than zero,
  3235.    then remove until none left. */
  3236. void
  3237. kill_self_indent (count)
  3238.      int count;
  3239. {
  3240.   /* Handle infinite case first. */
  3241.   if (count < 0)
  3242.     {
  3243.       output_column = 0;
  3244.       while (output_paragraph_offset)
  3245.     {
  3246.       if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  3247.         output_paragraph_offset--;
  3248.       else
  3249.         break;
  3250.     }
  3251.     }
  3252.   else
  3253.     {
  3254.       while (output_paragraph_offset && count--)
  3255.     if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  3256.       output_paragraph_offset--;
  3257.     else
  3258.       break;
  3259.     }
  3260. }
  3261.  
  3262. /* Non-zero means do not honor calls to flush_output (). */
  3263. static int flushing_ignored = 0;
  3264.  
  3265. /* Prevent calls to flush_output () from having any effect. */
  3266. void
  3267. inhibit_output_flushing ()
  3268. {
  3269.   flushing_ignored++;
  3270. }
  3271.  
  3272. void
  3273. /* Allow calls to flush_output () to write the paragraph data. */
  3274. uninhibit_output_flushing ()
  3275. {
  3276.   flushing_ignored--;
  3277. }
  3278.  
  3279. void
  3280. flush_output ()
  3281. {
  3282.   register size_t i;
  3283.   register unsigned char cc;
  3284. #ifdef atarist
  3285.   register size_t j = 0;
  3286. #endif
  3287.  
  3288.   if (!output_paragraph_offset || flushing_ignored)
  3289.     return;
  3290.  
  3291.   i = 0;
  3292.   while (i < output_paragraph_offset)
  3293.     {
  3294.       cc = output_paragraph[i];
  3295.       if (cc == (unsigned char)(' ' | 0x80) ||
  3296.       cc == (unsigned char)('\t' | 0x80) ||
  3297.       cc == (unsigned char)('\n' | 0x80) ||
  3298.       sentence_ender (UNMETA (cc)))
  3299.         {
  3300.       cc &= 0x7f;
  3301.       output_paragraph[i] = cc;
  3302.         }
  3303. #ifdef atarist
  3304.       if (cc == '\n')
  3305.         {
  3306.       /* Restore CR/LF brain damage */
  3307.       if ((i - j) != fwrite (&output_paragraph[j], (size_t)1,
  3308.                  (i - j), output_stream) ||
  3309.           /* in 'text' mode this really writes '\r\n' */
  3310.           EOF == fputc  ('\n', output_stream))
  3311.         {
  3312.           perror (output_filename);
  3313.           exit (FATAL);
  3314.         }
  3315.       i += 1;
  3316.       j = i;
  3317.         }
  3318.       else
  3319.     i += 1;
  3320. #else  /* !atarist */
  3321.       i += 1;
  3322. #endif /* !atarist */
  3323.     }
  3324. #ifndef atarist
  3325.   if (i != fwrite (output_paragraph, 1, i, output_stream))
  3326. #else  /* atarist */
  3327.   /* flush the remainder in a case when the last character in
  3328.    * output_paragraph was not '\n' */
  3329.   if ((i - j) != fwrite (&output_paragraph[j], (size_t)1,
  3330.              i - j, output_stream))
  3331. #endif /* atarist */
  3332.     {
  3333.       perror (output_filename);
  3334.       exit (FATAL);
  3335.     }
  3336.   output_position += i; /* i is equal now to output_paragraph_offset */
  3337.   output_paragraph_offset = 0;
  3338. }
  3339.  
  3340. /* How to close a paragraph controlling the number of lines between
  3341.    this one and the last one. */
  3342.  
  3343. /* Paragraph spacing is controlled by this variable.  It is the number of
  3344.    blank lines that you wish to appear between paragraphs.  A value of
  3345.    1 creates a single blank line between paragraphs. */
  3346. int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING;
  3347.  
  3348. /* Close the current paragraph, leaving no blank lines between them. */
  3349. void
  3350. close_single_paragraph ()
  3351. {
  3352.   close_paragraph_with_lines (0);
  3353. }
  3354.  
  3355. /* Close a paragraph after an insertion has ended. */
  3356. void
  3357. close_insertion_paragraph ()
  3358. {
  3359.   if (!insertion_paragraph_closed)
  3360.     {
  3361.       /* Close the current paragraph, breaking the line. */
  3362.       close_single_paragraph ();
  3363.  
  3364.       /* Start a new paragraph here, inserting whatever indention is correct
  3365.      for the now current insertion level (one above the one that we are
  3366.      ending). */
  3367.       start_paragraph ();
  3368.  
  3369.       /* Tell close_paragraph () that the previous line has already been
  3370.      broken, so it should insert one less newline. */
  3371.       line_already_broken = 1;
  3372.  
  3373.       /* Let functions such as add_char () know that we have already found a
  3374.      newline. */
  3375.       ignore_blank_line ();
  3376.     }
  3377.   else
  3378.     {
  3379.       /* If the insertion paragraph is closed already, then we are seeing
  3380.      two `@end' commands in a row.  Note that the first one we saw was
  3381.      handled in the first part of this if-then-else clause, and at that
  3382.      time start_paragraph () was called, partially to handle the proper
  3383.      indentation of the current line.  However, the indentation level
  3384.      may have just changed again, so we may have to outdent the current
  3385.      line to the new indentation level. */
  3386.       if (current_indent < output_column)
  3387.     kill_self_indent (output_column - current_indent);
  3388.     }
  3389.  
  3390.   insertion_paragraph_closed = 1;
  3391. }
  3392.  
  3393. void
  3394. close_paragraph_with_lines (lines)
  3395.      int lines;
  3396. {
  3397.   int old_spacing = paragraph_spacing;
  3398.   paragraph_spacing = lines;
  3399.   close_paragraph ();
  3400.   paragraph_spacing = old_spacing;
  3401. }
  3402.  
  3403. /* Close the currently open paragraph. */
  3404. void
  3405. close_paragraph ()
  3406. {
  3407.   register int i;
  3408.  
  3409.   /* The insertion paragraph is no longer closed. */
  3410.   insertion_paragraph_closed = 0;
  3411.  
  3412.   if (paragraph_is_open && !must_start_paragraph)
  3413.     {
  3414.       register size_t tindex;
  3415.       register int c;
  3416.  
  3417.       tindex = output_paragraph_offset;
  3418.  
  3419.       /* Back up to last non-newline/space character, forcing all such
  3420.      subsequent characters to be newlines.  This isn't strictly
  3421.      necessary, but a couple of functions use the presence of a newline
  3422.      to make decisions. */
  3423.       for (tindex = output_paragraph_offset - 1; (long) tindex >= 0; --tindex)
  3424.     {
  3425.       c = output_paragraph[tindex];
  3426.  
  3427.       if (c == ' '|| c == '\n')
  3428.         output_paragraph[tindex] = '\n';
  3429.       else
  3430.         break;
  3431.     }
  3432.  
  3433.       /* All trailing whitespace is ignored. */
  3434.       output_paragraph_offset = ++tindex;
  3435.  
  3436.       /* Break the line if that is appropriate. */
  3437.       if (paragraph_spacing >= 0)
  3438.     insert ('\n');
  3439.  
  3440.       /* Add as many blank lines as is specified in PARAGRAPH_SPACING. */
  3441.       if (!force_flush_right)
  3442.     {
  3443.       for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
  3444.         insert ('\n');
  3445.     }
  3446.  
  3447.       /* If we are doing flush right indentation, then do it now
  3448.      on the paragraph (really a single line). */
  3449.       if (force_flush_right)
  3450.     do_flush_right_indentation ();
  3451.  
  3452.       flush_output ();
  3453.       paragraph_is_open = 0;
  3454.       no_indent = 0;
  3455.       output_column = 0;
  3456.     }
  3457.   ignore_blank_line ();
  3458. }
  3459.  
  3460. /* Make the last line just read look as if it were only a newline. */
  3461. void
  3462. ignore_blank_line ()
  3463. {
  3464.   last_inserted_character = '\n';
  3465.   last_char_was_newline = 1;
  3466. }
  3467.  
  3468. /* Align the end of the text in output_paragraph with fill_column. */
  3469. void
  3470. do_flush_right_indentation ()
  3471. {
  3472.   char *temp;
  3473.   size_t temp_len;
  3474.  
  3475.   kill_self_indent (-1);
  3476.  
  3477.   if (output_paragraph[0] != '\n')
  3478.     {
  3479.       output_paragraph[output_paragraph_offset] = '\0';
  3480.  
  3481.       if (output_paragraph_offset < (size_t)fill_column)
  3482.     {
  3483.       register int i;
  3484.  
  3485.       if ((size_t)fill_column >= paragraph_buffer_len)
  3486.         output_paragraph =
  3487.           xrealloc (output_paragraph,
  3488.             (paragraph_buffer_len += (size_t)fill_column));
  3489.  
  3490.       temp_len = strlen ((char *)output_paragraph);
  3491.       temp = (char *)xmalloc (temp_len + 1);
  3492.       memcpy (temp, (char *)output_paragraph, temp_len);
  3493.  
  3494.       for (i = 0; i < (size_t)fill_column - output_paragraph_offset; i++)
  3495.         output_paragraph[i] = ' ';
  3496.  
  3497.       memcpy ((char *)output_paragraph + i, temp, temp_len);
  3498.       free (temp);
  3499.       output_paragraph_offset = (size_t)fill_column;
  3500.     }
  3501.     }
  3502. }
  3503.  
  3504. /* Begin a new paragraph. */
  3505. void
  3506. start_paragraph ()
  3507. {
  3508.   /* First close existing one. */
  3509.   if (paragraph_is_open)
  3510.     close_paragraph ();
  3511.  
  3512.   /* In either case, the insertion paragraph is no longer closed. */
  3513.   insertion_paragraph_closed = 0;
  3514.  
  3515.   /* However, the paragraph is open! */
  3516.   paragraph_is_open = 1;
  3517.  
  3518.   /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
  3519.      had to be called before we would allow any other paragraph operations
  3520.      to have an effect. */
  3521.   if (!must_start_paragraph)
  3522.     {
  3523.       int amount_to_indent = 0;
  3524.  
  3525.       /* If doing indentation, then insert the appropriate amount. */
  3526.       if (!no_indent)
  3527.     {
  3528.       if (inhibit_paragraph_indentation)
  3529.         {
  3530.           amount_to_indent = current_indent;
  3531.           if (inhibit_paragraph_indentation < 0)
  3532.         inhibit_paragraph_indentation++;
  3533.         }
  3534.       else if (paragraph_start_indent < 0)
  3535.         amount_to_indent = current_indent;
  3536.       else
  3537.         amount_to_indent = current_indent + paragraph_start_indent;
  3538.  
  3539.       if (amount_to_indent >= output_column)
  3540.         {
  3541.           amount_to_indent -= output_column;
  3542.           indent (amount_to_indent);
  3543.           output_column += amount_to_indent;
  3544.         }
  3545.     }
  3546.     }
  3547.   else
  3548.     must_start_paragraph = 0;
  3549. }
  3550.  
  3551. /* Insert the indentation specified by AMOUNT. */
  3552. void
  3553. indent (amount)
  3554.      int amount;
  3555. {
  3556.   register BRACE_ELEMENT *elt = brace_stack;
  3557.  
  3558.   /* For every START_POS saved within the brace stack which will be affected
  3559.      by this indentation, bump that start pos forward. */
  3560.   while (elt)
  3561.     {
  3562.       if (elt->pos >= output_paragraph_offset)
  3563.     elt->pos += amount;
  3564.       elt = elt->next;
  3565.     }
  3566.  
  3567.   while (--amount >= 0)
  3568.     insert (' ');
  3569. }
  3570.  
  3571. /* Search forward for STRING in input_text.
  3572.    FROM says where where to start. */
  3573. size_t
  3574. search_forward (string, from)
  3575.      const char *string;
  3576.      size_t from;
  3577. {
  3578.   size_t len = strlen (string);
  3579.  
  3580.   while (from < size_of_input_text)
  3581.     {
  3582.       if (strncmp (input_text + from, string, len) == 0)
  3583.     return (from);
  3584.       from++;
  3585.     }
  3586.   return (-1);
  3587. }
  3588.  
  3589. /* Whoops, Unix doesn't have stricmp. */
  3590.  
  3591. /* Case independent string compare. */
  3592. int
  3593. stricmp (string1, string2)
  3594.      const char *string1, *string2;
  3595. {
  3596.   char ch1, ch2;
  3597.  
  3598.   for (;;)
  3599.     {
  3600.       ch1 = *string1++;
  3601.       ch2 = *string2++;
  3602.  
  3603.       if (!(ch1 | ch2))
  3604.     return (0);
  3605.  
  3606.       ch1 = coerce_to_upper (ch1);
  3607.       ch2 = coerce_to_upper (ch2);
  3608.  
  3609.       if (ch1 != ch2)
  3610.     return (ch1 - ch2);
  3611.     }
  3612. }
  3613.  
  3614. /** already defined 
  3615. enum insertion_type { menu, quotation, lisp, smalllisp, example,
  3616.   smallexample, display, itemize, format, enumerate, cartouche, table,
  3617.   ftable, vtable, group, ifinfo, flushleft, flushright, ifset, ifclear, deffn,
  3618.   defun, defmac, defspec, defvr, defvar, defopt, deftypefn,
  3619.   deftypefun, deftypevr, deftypevar, defcv, defivar, defop, defmethod,
  3620.   deftypemethod, deftp, bad_type };
  3621. **/
  3622.  
  3623. const char *insertion_type_names[] = { "menu", "quotation", "lisp",
  3624.   "smalllisp", "example", "smallexample", "display", "itemize",
  3625.   "format", "enumerate", "cartouche", "table", "ftable", "vtable", "group",
  3626.   "ifinfo", "flushleft", "flushright", "ifset", "ifclear", "deffn",
  3627.   "defun", "defmac", "defspec", "defvr", "defvar", "defopt",
  3628.   "deftypefn", "deftypefun", "deftypevr", "deftypevar", "defcv",
  3629.   "defivar", "defop", "defmethod", "deftypemethod", "deftp",
  3630.   "bad_type" };
  3631.  
  3632. int insertion_level = 0;
  3633. /** already defined
  3634. typedef struct istack_elt
  3635. {
  3636.   struct istack_elt *next;
  3637.   char *item_function;
  3638.   int line_number;
  3639.   int filling_enabled;
  3640.   int indented_fill;
  3641.   enum insertion_type insertion;
  3642.   int inhibited;
  3643. } INSERTION_ELT;
  3644. **/
  3645. INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL;
  3646.  
  3647. void
  3648. init_insertion_stack ()
  3649. {
  3650.   insertion_stack = (INSERTION_ELT *) NULL;
  3651. }
  3652.  
  3653. /* Return the type of the current insertion. */
  3654. enum insertion_type
  3655. current_insertion_type ()
  3656. {
  3657.   if (!insertion_level)
  3658.     return (bad_type);
  3659.   else
  3660.     return (insertion_stack->insertion);
  3661. }
  3662.  
  3663. /* Return a pointer to the string which is the function to wrap around
  3664.    items. */
  3665. char *
  3666. current_item_function ()
  3667. {
  3668.   register int level, done;
  3669.   register INSERTION_ELT *elt;
  3670.  
  3671.   level = insertion_level;
  3672.   elt = insertion_stack;
  3673.   done = 0;
  3674.  
  3675.   /* Skip down through the stack until we find a non-conditional insertion. */
  3676.   while (!done)
  3677.     {
  3678.       switch (elt->insertion)
  3679.     {
  3680.     case ifinfo:
  3681.     case ifset:
  3682.     case ifclear:
  3683.     case cartouche:
  3684.       elt = elt->next;
  3685.       level--;
  3686.       break;
  3687.  
  3688.     default:
  3689.       done = 1;
  3690.     }
  3691.     }
  3692.  
  3693.   if (!level)
  3694.     return ((char *) NULL);
  3695.   else
  3696.     return (elt->item_function);
  3697. }
  3698.  
  3699. char *
  3700. get_item_function ()
  3701. {
  3702.   char *item_function;
  3703.   get_rest_of_line (&item_function);
  3704.   backup_input_pointer ();
  3705.   canon_white (item_function);
  3706.   return (item_function);
  3707. }
  3708.  
  3709.  /* Push the state of the current insertion on the stack. */
  3710. void
  3711. push_insertion (type, item_function)
  3712.      enum insertion_type type;
  3713.      char *item_function;
  3714. {
  3715.   INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT));
  3716.  
  3717.   new->item_function = item_function;
  3718.   new->filling_enabled = filling_enabled;
  3719.   new->indented_fill = indented_fill;
  3720.   new->insertion = type;
  3721.   new->line_number = line_number;
  3722.   new->inhibited = inhibit_paragraph_indentation;
  3723.   new->next = insertion_stack;
  3724.   insertion_stack = new;
  3725.   insertion_level++;
  3726. }
  3727.  
  3728.  /* Pop the value on top of the insertion stack into the
  3729.     global variables. */
  3730. void
  3731. pop_insertion ()
  3732. {
  3733.   INSERTION_ELT *temp = insertion_stack;
  3734.  
  3735.   if (temp == (INSERTION_ELT *) NULL)
  3736.     return;
  3737.  
  3738.   inhibit_paragraph_indentation = temp->inhibited;
  3739.   filling_enabled = temp->filling_enabled;
  3740.   indented_fill = temp->indented_fill;
  3741.   free_and_clear (&(temp->item_function));
  3742.   insertion_stack = insertion_stack->next;
  3743.   free (temp);
  3744.   insertion_level--;
  3745. }
  3746.  
  3747.  /* Return a pointer to the print name of this
  3748.     enumerated type. */
  3749. const char *
  3750. insertion_type_pname (type)
  3751.      enum insertion_type type;
  3752. {
  3753.   if ((int) type < (int) bad_type)
  3754.     return (insertion_type_names[(int) type]);
  3755.   else
  3756.     return ("Broken-Type in insertion_type_pname");
  3757. }
  3758.  
  3759. /* Return the insertion_type associated with NAME.
  3760.    If the type is not one of the known ones, return BAD_TYPE. */
  3761. enum insertion_type
  3762. find_type_from_name (name)
  3763.      char *name;
  3764. {
  3765.   int index = 0;
  3766.   while (index < (int) bad_type)
  3767.     {
  3768.       if (strcmp (name, insertion_type_names[index]) == 0)
  3769.     return (enum insertion_type) index;
  3770.       index++;
  3771.     }
  3772.   return (bad_type);
  3773. }
  3774.  
  3775. void
  3776. do_nothing ()
  3777. {
  3778. }
  3779.  
  3780. int
  3781. defun_insertion (type)
  3782.      enum insertion_type type;
  3783. {
  3784.   return
  3785.     ((type == deffn)
  3786.      || (type == defun)
  3787.      || (type == defmac)
  3788.      || (type == defspec)
  3789.      || (type == defvr)
  3790.      || (type == defvar)
  3791.      || (type == defopt)
  3792.      || (type == deftypefn)
  3793.      || (type == deftypefun)
  3794.      || (type == deftypevr)
  3795.      || (type == deftypevar)
  3796.      || (type == defcv)
  3797.      || (type == defivar)
  3798.      || (type == defop)
  3799.      || (type == defmethod)
  3800.      || (type == deftypemethod)
  3801.      || (type == deftp));
  3802. }
  3803.  
  3804. /* MAX_NS is the maximum nesting level for enumerations.  I picked 100
  3805.    which seemed reasonable.  This doesn't control the number of items,
  3806.    just the number of nested lists. */
  3807. /** already defined
  3808. #define max_stack_depth 100
  3809. #define ENUM_DIGITS 1
  3810. #define ENUM_ALPHA  2
  3811. typedef struct {
  3812.   int enumtype;
  3813.   int enumval;
  3814. } DIGIT_ALPHA;
  3815. **/
  3816.  
  3817. DIGIT_ALPHA enumstack[max_stack_depth];
  3818. int enumstack_offset = 0;
  3819. int current_enumval = 1;
  3820. int current_enumtype = ENUM_DIGITS;
  3821. char *enumeration_arg = (char *)NULL;
  3822.  
  3823. void
  3824. start_enumerating (at, type)
  3825.      int at, type;
  3826. {
  3827.   if ((enumstack_offset + 1) == max_stack_depth)
  3828.     {
  3829.       line_error ("Enumeration stack overflow");
  3830.       return;
  3831.     }
  3832.   enumstack[enumstack_offset].enumtype = current_enumtype;
  3833.   enumstack[enumstack_offset].enumval = current_enumval;
  3834.   enumstack_offset++;
  3835.   current_enumval = at;
  3836.   current_enumtype = type;
  3837. }
  3838.  
  3839. void
  3840. stop_enumerating ()
  3841. {
  3842.   --enumstack_offset;
  3843.   if (enumstack_offset < 0)
  3844.     enumstack_offset = 0;
  3845.  
  3846.   current_enumval = enumstack[enumstack_offset].enumval;
  3847.   current_enumtype = enumstack[enumstack_offset].enumtype;
  3848. }
  3849.  
  3850. /* Place a letter or digits into the output stream. */
  3851. void
  3852. enumerate_item ()
  3853. {
  3854.   char temp[10];
  3855.  
  3856.   if (current_enumtype == ENUM_ALPHA)
  3857.     {
  3858.       if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1))
  3859.     {
  3860.       current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A');
  3861.       warning ("Lettering overflow, restarting at %c", current_enumval);
  3862.     }
  3863.       sprintf (temp, "%c. ", current_enumval);
  3864.     }
  3865.   else
  3866.     sprintf (temp, "%d. ", current_enumval);
  3867.  
  3868.   indent (output_column += (current_indent - strlen (temp)));
  3869.   add_word (temp);
  3870.   current_enumval++;
  3871. }
  3872.  
  3873. /* This is where the work for all the "insertion" style
  3874.    commands is done.  A huge switch statement handles the
  3875.    various setups, and generic code is on both sides. */
  3876. void
  3877. begin_insertion (type)
  3878.      enum insertion_type type;
  3879. {
  3880.   int no_discard = 0;
  3881.  
  3882.   if (defun_insertion (type))
  3883.     {
  3884.       push_insertion (type, savestring (""));
  3885.       no_discard++;
  3886.     }
  3887.   else
  3888.     push_insertion (type, get_item_function ());
  3889.  
  3890.   switch (type)
  3891.     {
  3892.     case menu:
  3893.       if (!no_headers)
  3894.     close_paragraph ();
  3895.  
  3896.       filling_enabled = no_indent = 0;
  3897.       inhibit_paragraph_indentation = 1;
  3898.  
  3899.       if (!no_headers)
  3900.     add_word ("* Menu:\n");
  3901.  
  3902.       in_menu++;
  3903.       no_discard++;
  3904.       break;
  3905.  
  3906.       /* I think @quotation is meant to do filling.
  3907.      If you don't want filling, then use @example. */
  3908.     case quotation:
  3909.       close_single_paragraph ();
  3910.       last_char_was_newline = no_indent = 0;
  3911.       indented_fill = filling_enabled = 1;
  3912.       inhibit_paragraph_indentation = 1;
  3913.       current_indent += default_indentation_increment;
  3914.       break;
  3915.  
  3916.     case display:
  3917.     case example:
  3918.     case smallexample:
  3919.     case lisp:
  3920.     case smalllisp:
  3921.       /* Just like @example, but no indentation. */
  3922.     case format:
  3923.  
  3924.       close_single_paragraph ();
  3925.       inhibit_paragraph_indentation = 1;
  3926.       in_fixed_width_font++;
  3927.       filling_enabled = 0;
  3928.       last_char_was_newline = 0;
  3929.  
  3930.       if (type != format)
  3931.     current_indent += default_indentation_increment;
  3932.  
  3933.       break;
  3934.  
  3935.     case table:
  3936.     case ftable:
  3937.     case vtable:
  3938.     case itemize:
  3939.       close_single_paragraph ();
  3940.       current_indent += default_indentation_increment;
  3941.       filling_enabled = indented_fill = 1;
  3942. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  3943.       inhibit_paragraph_indentation = 0;
  3944. #else
  3945.       inhibit_paragraph_indentation = 1;
  3946. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  3947.  
  3948.       /* Make things work for losers who forget the itemize syntax. */
  3949.       if (type == itemize)
  3950.     {
  3951.       if (!(*insertion_stack->item_function))
  3952.         {
  3953.           free (insertion_stack->item_function);
  3954.           insertion_stack->item_function = savestring ("@bullet");
  3955.         }
  3956.     }
  3957.       break;
  3958.  
  3959.     case enumerate:
  3960.       close_single_paragraph ();
  3961.       no_indent = 0;
  3962. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  3963.       inhibit_paragraph_indentation = 0;
  3964. #else
  3965.       inhibit_paragraph_indentation = 1;
  3966. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  3967.  
  3968.       current_indent += default_indentation_increment;
  3969.       filling_enabled = indented_fill = 1;
  3970.  
  3971.       if (isdigit (*enumeration_arg))
  3972.     start_enumerating (atoi (enumeration_arg), ENUM_DIGITS);
  3973.       else
  3974.     start_enumerating (*enumeration_arg, ENUM_ALPHA);
  3975.       break;
  3976.  
  3977.       /* Does nothing special in makeinfo. */
  3978.     case group:
  3979.       /* Only close the paragraph if we are not inside of an @example. */
  3980.       if (!insertion_stack->next ||
  3981.       insertion_stack->next->insertion != example)
  3982.     close_single_paragraph ();
  3983.       break;
  3984.  
  3985.       /* Insertions that are no-ops in info, but do something in TeX. */
  3986.     case ifinfo:
  3987.     case ifset:
  3988.     case ifclear:
  3989.     case cartouche:
  3990.       if (in_menu)
  3991.     no_discard++;
  3992.       break;
  3993.  
  3994.     case deffn:
  3995.     case defun:
  3996.     case defmac:
  3997.     case defspec:
  3998.     case defvr:
  3999.     case defvar:
  4000.     case defopt:
  4001.     case deftypefn:
  4002.     case deftypefun:
  4003.     case deftypevr:
  4004.     case deftypevar:
  4005.     case defcv:
  4006.     case defivar:
  4007.     case defop:
  4008.     case defmethod:
  4009.     case deftypemethod:
  4010.     case deftp:
  4011.       inhibit_paragraph_indentation = 1;
  4012.       filling_enabled = indented_fill = 1;
  4013.       current_indent += default_indentation_increment;
  4014.       no_indent = 0;
  4015.       break;
  4016.  
  4017.     case flushleft:
  4018.       close_single_paragraph ();
  4019.       inhibit_paragraph_indentation = 1;
  4020.       filling_enabled = indented_fill = no_indent = 0;
  4021.       break;
  4022.  
  4023.     case flushright:
  4024.       close_single_paragraph ();
  4025.       filling_enabled = indented_fill = no_indent = 0;
  4026.       inhibit_paragraph_indentation = 1;
  4027.       force_flush_right++;
  4028.       break;
  4029.     default:
  4030.       break;
  4031.     }
  4032.  
  4033.   if (!no_discard)
  4034.     discard_until ("\n");
  4035. }
  4036.  
  4037. /* Try to end the insertion with the specified TYPE.
  4038.    TYPE, with a value of bad_type,  gets translated to match
  4039.    the value currently on top of the stack.
  4040.    Otherwise, if TYPE doesn't match the top of the insertion stack,
  4041.    give error. */
  4042. void
  4043. end_insertion (type)
  4044.      enum insertion_type type;
  4045. {
  4046.   enum insertion_type temp_type;
  4047.  
  4048.   if (!insertion_level)
  4049.     return;
  4050.  
  4051.   temp_type = current_insertion_type ();
  4052.  
  4053.   if (type == bad_type)
  4054.     type = temp_type;
  4055.  
  4056.   if (type != temp_type)
  4057.     {
  4058.       line_error
  4059.     ("`%cend' expected `%s', but saw `%s'.", COMMAND_PREFIX,
  4060.      insertion_type_pname (temp_type), insertion_type_pname (type));
  4061.       return;
  4062.     }
  4063.  
  4064.   pop_insertion ();
  4065.  
  4066.   switch (type)
  4067.     {
  4068.       /* Insertions which have no effect on paragraph formatting. */
  4069.     case ifinfo:
  4070.     case ifset:
  4071.     case ifclear:
  4072.       break;
  4073.  
  4074.     case menu:
  4075.       in_menu--;        /* No longer hacking menus. */
  4076.       if (!no_headers)
  4077.     close_insertion_paragraph ();
  4078.       break;
  4079.  
  4080.     case enumerate:
  4081.       stop_enumerating ();
  4082.       close_insertion_paragraph ();
  4083.       current_indent -= default_indentation_increment;
  4084.       break;
  4085.  
  4086.     case flushleft:
  4087.     case group:
  4088.     case cartouche:
  4089.       close_insertion_paragraph ();
  4090.       break;
  4091.  
  4092.     case format:
  4093.     case display:
  4094.     case example:
  4095.     case smallexample:
  4096.     case lisp:
  4097.     case smalllisp:
  4098.     case quotation:
  4099.  
  4100.       /* @quotation is the only one of the above without a fixed width
  4101.      font. */
  4102.       if (type != quotation)
  4103.     in_fixed_width_font--;
  4104.  
  4105.       /* @format is the only fixed_width insertion without a change
  4106.      in indentation. */
  4107.       if (type != format)
  4108.     current_indent -= default_indentation_increment;
  4109.  
  4110.       /* The ending of one of these insertions always marks the
  4111.      start of a new paragraph. */
  4112.       close_insertion_paragraph ();
  4113.       break;
  4114.  
  4115.     case table:
  4116.     case ftable:
  4117.     case vtable:
  4118.     case itemize:
  4119.       current_indent -= default_indentation_increment;
  4120.       break;
  4121.  
  4122.     case flushright:
  4123.       force_flush_right--;
  4124.       close_insertion_paragraph ();
  4125.       break;
  4126.  
  4127.       /* Handle the @defun style insertions with a default clause. */
  4128.     default:
  4129.       current_indent -= default_indentation_increment;
  4130.       close_insertion_paragraph ();
  4131.       break;
  4132.     }
  4133. }
  4134.  
  4135. /* Insertions cannot cross certain boundaries, such as node beginnings.  In
  4136.    code that creates such boundaries, you should call discard_insertions ()
  4137.    before doing anything else.  It prints the errors for you, and cleans up
  4138.    the insertion stack. */
  4139. void
  4140. discard_insertions ()
  4141. {
  4142.   size_t real_line_number = line_number;
  4143.   while (insertion_stack)
  4144.     {
  4145.       if (insertion_stack->insertion == ifinfo ||
  4146.       insertion_stack->insertion == ifset ||
  4147.       insertion_stack->insertion == ifclear ||
  4148.       insertion_stack->insertion == cartouche)
  4149.     break;
  4150.       else
  4151.     {
  4152.       char *offender = (char *)
  4153.         insertion_type_pname (insertion_stack->insertion);
  4154.  
  4155.       line_number = insertion_stack->line_number;
  4156.       line_error ("This `%s' doesn't have a matching `%cend %s'", offender,
  4157.               COMMAND_PREFIX, offender);
  4158.       pop_insertion ();
  4159.     }
  4160.     }
  4161.   line_number = real_line_number;
  4162. }
  4163.  
  4164. /* The actual commands themselves. */
  4165.  
  4166. /* Commands which insert themselves. */
  4167. void
  4168. insert_self ()
  4169. {
  4170.   add_word (command);
  4171. }
  4172.  
  4173. /* Force a line break in the output. */
  4174. void
  4175. cm_asterisk ()
  4176. {
  4177.   close_single_paragraph ();
  4178. #if !defined (ASTERISK_NEW_PARAGRAPH)
  4179.   cm_noindent ();
  4180. #endif /* ASTERISK_NEW_PARAGRAPH */  
  4181. }
  4182.  
  4183. /* Insert ellipsis. */
  4184. void
  4185. cm_dots (arg)
  4186.      int arg;
  4187. {
  4188.   if (arg == START)
  4189.     add_word ("...");
  4190. }
  4191.  
  4192. void
  4193. cm_bullet (arg)
  4194.      int arg;
  4195. {
  4196.   if (arg == START)
  4197.     add_char ('*');
  4198. }
  4199.  
  4200. void
  4201. cm_minus (arg)
  4202.      int arg;
  4203. {
  4204.   if (arg == START)
  4205.     add_char ('-');
  4206. }
  4207.  
  4208. /* Insert "TeX". */
  4209. void
  4210. cm_TeX (arg)
  4211.      int arg;
  4212. {
  4213.   if (arg == START)
  4214.     add_word ("TeX");
  4215. }
  4216.  
  4217. void
  4218. cm_copyright (arg)
  4219.      int arg;
  4220. {
  4221.   if (arg == START)
  4222.     add_word ("(C)");
  4223. }
  4224.  
  4225. void
  4226. cm_today (arg)
  4227.      int arg;
  4228. {
  4229.   static const char * months [12] =
  4230.     { "January", "February", "March", "April", "May", "June", "July",
  4231.     "August", "September", "October", "November", "December" };
  4232.   if (arg == START)
  4233.     {
  4234.       long timer = (time (0));
  4235.       struct tm *ts = (localtime (&timer));
  4236.       add_word_args
  4237.     ("%d %s %d",
  4238.      (ts -> tm_mday),
  4239.      (months [ts -> tm_mon]),
  4240.      ((ts -> tm_year) + 1900));
  4241.     }
  4242. }
  4243.  
  4244. void
  4245. cm_code (arg)
  4246.      int arg;
  4247. {
  4248.   extern int printing_index;
  4249.  
  4250.   if (printing_index)
  4251.     return;
  4252.  
  4253.   if (arg == START)
  4254.     {
  4255.       in_fixed_width_font++;
  4256.       add_char ('`');
  4257.     }
  4258.   else
  4259.     {
  4260.       add_word ("'");
  4261.       in_fixed_width_font--;
  4262.     }
  4263. }
  4264.  
  4265. void
  4266. cm_samp (arg)
  4267.      int arg;
  4268. {
  4269.   cm_code (arg);
  4270. }
  4271.  
  4272. void
  4273. cm_file (arg)
  4274.      int arg;
  4275. {
  4276.   cm_code (arg);
  4277. }
  4278.  
  4279. void
  4280. cm_kbd (arg)
  4281.      int arg;
  4282. {
  4283.   cm_code (arg);
  4284. }
  4285.  
  4286. void
  4287. cm_key (arg)
  4288.      int arg;
  4289. {
  4290. }
  4291.  
  4292. /* Convert the character at position into CTL. */
  4293. void
  4294. cm_ctrl (arg, position)
  4295.      int arg;
  4296.      size_t position;
  4297. {
  4298.   if (arg == END)
  4299.     output_paragraph[position - 1] = CTL (output_paragraph[position]);
  4300. }
  4301.  
  4302. /* Small Caps in makeinfo just does all caps. */
  4303. void
  4304. cm_sc (arg, start_pos, end_pos)
  4305.      int arg;
  4306.      size_t start_pos, end_pos;
  4307. {
  4308.   if (arg == END)
  4309.     {
  4310.       while (start_pos < end_pos)
  4311.     {
  4312.       output_paragraph[start_pos] =
  4313.         coerce_to_upper (output_paragraph[start_pos]);
  4314.       start_pos++;
  4315.     }
  4316.     }
  4317. }
  4318.  
  4319. /* @var in makeinfo just uppercases the text. */
  4320. void
  4321. cm_var (arg, start_pos, end_pos)
  4322.      int arg;
  4323.      size_t start_pos, end_pos;
  4324. {
  4325.   if (arg == END)
  4326.     {
  4327.       while (start_pos < end_pos)
  4328.     {
  4329.       output_paragraph[start_pos] =
  4330.         coerce_to_upper (output_paragraph[start_pos]);
  4331.       start_pos++;
  4332.     }
  4333.     }
  4334. }
  4335.  
  4336. void
  4337. cm_dfn (arg, position)
  4338.      int arg;
  4339.      size_t position;
  4340. {
  4341.   add_char ('"');
  4342. }
  4343.  
  4344. void
  4345. cm_emph (arg)
  4346.      int arg;
  4347. {
  4348.   add_char ('*');
  4349. }
  4350.  
  4351. void
  4352. cm_strong (arg, position)
  4353.      int arg;
  4354.      size_t position;
  4355. {
  4356.   cm_emph (arg);
  4357. }
  4358.  
  4359. void
  4360. cm_cite (arg, position)
  4361.      int arg;
  4362.      size_t position;
  4363. {
  4364.   if (arg == START)
  4365.     add_word ("`");
  4366.   else
  4367.     add_word ("'");
  4368. }
  4369.  
  4370. /* Current text is italicized. */
  4371. void
  4372. cm_italic (arg, start, end)
  4373.      int arg, start, end;
  4374. {
  4375. }
  4376.  
  4377. /* Current text is highlighted. */
  4378. void
  4379. cm_bold (arg, start, end)
  4380.      int arg, start, end;
  4381. {
  4382.   cm_italic (arg, start, end);
  4383. }
  4384.  
  4385. /* Current text is in roman font. */
  4386. void
  4387. cm_roman (arg, start, end)
  4388.      int arg, start, end;
  4389. {
  4390. }
  4391.  
  4392. /* Current text is in roman font. */
  4393. void
  4394. cm_titlefont (arg, start, end)
  4395.      int arg, start, end;
  4396. {
  4397. }
  4398.  
  4399. /* Italicize titles. */
  4400. void
  4401. cm_title (arg, start, end)
  4402.      int arg, start, end;
  4403. {
  4404.   cm_italic (arg, start, end);
  4405. }
  4406.  
  4407. /* @refill is a NOP. */
  4408. void
  4409. cm_refill ()
  4410. {
  4411. }
  4412.  
  4413. /* Prevent the argument from being split across two lines. */
  4414. void
  4415. cm_w (arg, start, end)
  4416.      int arg, start, end;
  4417. {
  4418.   if (arg == START)
  4419.     non_splitting_words++;
  4420.   else
  4421.     non_splitting_words--;
  4422. }
  4423.  
  4424.  
  4425. /* Explain that this command is obsolete, thus the user shouldn't
  4426.    do anything with it. */
  4427. void
  4428. cm_obsolete (arg, start, end)
  4429.      int arg, start, end;
  4430. {
  4431.   if (arg == START)
  4432.     warning ("The command `@%s' is obsolete", command);
  4433. }
  4434.  
  4435. /* Insert the text following input_text_offset up to the end of the line
  4436.    in a new, separate paragraph.  Directly underneath it, insert a
  4437.    line of WITH_CHAR, the same length of the inserted text. */
  4438. void
  4439. insert_and_underscore (with_char)
  4440.      int with_char;
  4441. {
  4442.   int len, i, old_no_indent;
  4443.   int starting_pos, ending_pos;
  4444.   char *temp;
  4445.  
  4446.   close_paragraph ();
  4447.   filling_enabled =  indented_fill = 0;
  4448.   old_no_indent = no_indent;
  4449.   no_indent = 1;
  4450.   get_rest_of_line (&temp);
  4451.  
  4452.   starting_pos = output_position + output_paragraph_offset;
  4453.   execute_string ("%s\n", temp);
  4454.   ending_pos = output_position + output_paragraph_offset;
  4455.   free (temp);
  4456.  
  4457.   len = (ending_pos - starting_pos) - 1;
  4458.   for (i = 0; i < len; i++)
  4459.     add_char (with_char);
  4460.   insert ('\n');
  4461.   close_paragraph ();
  4462.   filling_enabled = 1;
  4463.   no_indent = old_no_indent;
  4464. }
  4465.  
  4466. /* Here is a structure which associates sectioning commands with
  4467.    an integer, hopefully to reflect the `depth' of the current
  4468.    section. */
  4469. struct {
  4470.   char *name;
  4471.   int level;
  4472. } section_alist[] = {
  4473.   { "unnumberedsubsubsec", 5 },
  4474.   { "unnumberedsubsec", 4 },
  4475.   { "unnumberedsec", 3 },
  4476.   { "unnumbered", 2 },
  4477.   { "appendixsubsubsec", 5 },
  4478.   { "appendixsubsec", 4 },
  4479.   { "appendixsec", 3 },
  4480.   { "appendixsection", 3 },
  4481.   { "appendix", 2 },
  4482.   { "subsubsec", 5 },
  4483.   { "subsubsection", 5 },
  4484.   { "subsection", 4 },
  4485.   { "section", 3 },
  4486.   { "chapter", 2 },
  4487.   { "top", 1 },
  4488.  
  4489.   { (char *)NULL, 0 }
  4490. };
  4491.  
  4492. /* Amount to offset the name of sectioning commands to levels by. */
  4493. int section_alist_offset = 0;
  4494.  
  4495. /* Shift the meaning of @section to @chapter. */
  4496. void
  4497. cm_raisesections ()
  4498. {
  4499.   discard_until ("\n");
  4500.   section_alist_offset--;
  4501. }
  4502.  
  4503. /* Shift the meaning of @chapter to @section. */
  4504. void
  4505. cm_lowersections ()
  4506. {
  4507.   discard_until ("\n");
  4508.   section_alist_offset++;
  4509. }
  4510.  
  4511. /* Return an integer which identifies the type section present in TEXT. */
  4512. int
  4513. what_section (text)
  4514.      char *text;
  4515. {
  4516.   register int i, j;
  4517.   const char *t;
  4518.  
  4519.  find_section_command:
  4520.   for (j = 0; text[j] && cr_or_whitespace (text[j]); j++);
  4521.   if (text[j] != '@')
  4522.     return (-1);
  4523.  
  4524.   text = text + j + 1;
  4525.  
  4526.   /* We skip @c, @comment, and @?index commands. */
  4527.   if ((strncmp (text, "comment", strlen ("comment")) == 0) ||
  4528.       (text[0] == 'c' && cr_or_whitespace (text[1])) ||
  4529.       (strcmp (text + 1, "index") == 0))
  4530.     {
  4531.       while (*text++ != '\n');
  4532.       goto find_section_command;
  4533.     }
  4534.  
  4535.   /* Handle italicized sectioning commands. */
  4536.   if (*text == 'i')
  4537.     text++;
  4538.  
  4539.   for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++);
  4540.  
  4541.   for (i = 0; (t = section_alist[i].name); i++)
  4542.     {
  4543.       if (j == strlen (t) && strncmp (t, text, j) == 0)
  4544.     {
  4545.       int return_val;
  4546.  
  4547.       return_val = (section_alist[i].level + section_alist_offset);
  4548.  
  4549.       if (return_val < 0)
  4550.         return_val = 0;
  4551.       else if (return_val > 5)
  4552.         return_val = 5;
  4553.       return (return_val);
  4554.     }
  4555.     }
  4556.   return (-1);
  4557. }
  4558.  
  4559. /* Treat this just like @unnumbered.  The only difference is
  4560.    in node defaulting. */
  4561. void
  4562. cm_top ()
  4563. {
  4564.   static int top_encountered = 0;
  4565.   cm_unnumbered ();
  4566.  
  4567.   /* It is an error to have more than one @top. */
  4568.   if (top_encountered)
  4569.     {
  4570.       TAG_ENTRY *tag = tag_table;
  4571.  
  4572.       line_error ("There already is a node having @top as a section");
  4573.  
  4574.       while (tag != (TAG_ENTRY *)NULL)
  4575.     {
  4576.       if ((tag->flags & IS_TOP))
  4577.         {
  4578.           size_t old_line_number = line_number;
  4579.           char *old_input_filename = input_filename;
  4580.  
  4581.           line_number = tag->line_no;
  4582.           input_filename = tag->filename;
  4583.           line_error ("Here is the @top node.");
  4584.           input_filename = old_input_filename;
  4585.           line_number = old_line_number;
  4586.           return;
  4587.         }
  4588.       tag = tag->next_ent;
  4589.     }
  4590.     }
  4591.   else
  4592.     {
  4593.       top_encountered = 1;
  4594.  
  4595.       /* The most recently defined node is the top node. */
  4596.       if (tag_table)
  4597.     tag_table->flags |= IS_TOP;
  4598.  
  4599.       /* Now set the logical hierarchical level of the Top node. */
  4600.       {
  4601.     size_t orig_offset = input_text_offset;
  4602.  
  4603.     input_text_offset = search_forward ("\n@node", orig_offset);
  4604.  
  4605.     if ((long)input_text_offset > 0)
  4606.       {
  4607.         int this_section;
  4608.  
  4609.         /* Move to the end of this line, and find out what the
  4610.            sectioning command is here. */
  4611.         while (input_text[input_text_offset] != '\n')
  4612.           input_text_offset++;
  4613.  
  4614.         if (input_text_offset < size_of_input_text)
  4615.           input_text_offset++;
  4616.  
  4617.         this_section = what_section (input_text + input_text_offset);
  4618.  
  4619.         /* If we found a sectioning command, then give the top section
  4620.            a level of this section - 1. */
  4621.         if (this_section != -1)
  4622.           {
  4623.         register int i;
  4624.  
  4625.         for (i = 0; section_alist[i].name; i++)
  4626.           if (strcmp (section_alist[i].name, "Top") == 0)
  4627.             {
  4628.               section_alist[i].level = this_section - 1;
  4629.               break;
  4630.             }
  4631.           }
  4632.       }
  4633.     input_text_offset = orig_offset;
  4634.       }
  4635.     }
  4636. }
  4637.  
  4638. /* Organized by level commands.  That is, "*" == chapter, "=" == section. */
  4639. char *scoring_characters = "*=-.";
  4640.  
  4641. void
  4642. sectioning_underscore (command)
  4643.      char *command;
  4644. {
  4645.   char character;
  4646.   int level;
  4647.  
  4648.   level = what_section (command);
  4649.   level -= 2;
  4650.  
  4651.   if (level < 0)
  4652.     level = 0;
  4653.  
  4654.   character = scoring_characters[level];
  4655.  
  4656.   insert_and_underscore (character);
  4657. }
  4658.  
  4659. /* The remainder of the text on this line is a chapter heading. */
  4660. void
  4661. cm_chapter ()
  4662. {
  4663.   sectioning_underscore ("@chapter");
  4664. }
  4665.  
  4666. /* The remainder of the text on this line is a section heading. */
  4667. void
  4668. cm_section ()
  4669. {
  4670.   sectioning_underscore ("@section");
  4671. }
  4672.  
  4673. /* The remainder of the text on this line is a subsection heading. */
  4674. void
  4675. cm_subsection ()
  4676. {
  4677.   sectioning_underscore ("@subsection");
  4678. }
  4679.  
  4680. /* The remainder of the text on this line is a subsubsection heading. */
  4681. void
  4682. cm_subsubsection ()
  4683. {
  4684.   sectioning_underscore ("@subsubsection");
  4685. }
  4686.  
  4687. /* The remainder of the text on this line is an unnumbered heading. */
  4688. void
  4689. cm_unnumbered ()
  4690. {
  4691.   cm_chapter ();
  4692. }
  4693.  
  4694. /* The remainder of the text on this line is an unnumbered section heading. */
  4695. void
  4696. cm_unnumberedsec ()
  4697. {
  4698.   cm_section ();
  4699. }
  4700.  
  4701. /* The remainder of the text on this line is an unnumbered
  4702.    subsection heading. */
  4703. void
  4704. cm_unnumberedsubsec ()
  4705. {
  4706.   cm_subsection ();
  4707. }
  4708.  
  4709. /* The remainder of the text on this line is an unnumbered
  4710.    subsubsection heading. */
  4711. void
  4712. cm_unnumberedsubsubsec ()
  4713. {
  4714.   cm_subsubsection ();
  4715. }
  4716.  
  4717. /* The remainder of the text on this line is an appendix heading. */
  4718. void
  4719. cm_appendix ()
  4720. {
  4721.   cm_chapter ();
  4722. }
  4723.  
  4724. /* The remainder of the text on this line is an appendix section heading. */
  4725. void
  4726. cm_appendixsec ()
  4727. {
  4728.   cm_section ();
  4729. }
  4730.  
  4731. /* The remainder of the text on this line is an appendix subsection heading. */
  4732. void
  4733. cm_appendixsubsec ()
  4734. {
  4735.   cm_subsection ();
  4736. }
  4737.  
  4738. /* The remainder of the text on this line is an appendix
  4739.    subsubsection heading. */
  4740. void
  4741. cm_appendixsubsubsec ()
  4742. {
  4743.   cm_subsubsection ();
  4744. }
  4745.  
  4746. /* Compatibility functions substitute for chapter, section, etc. */
  4747. void
  4748. cm_majorheading ()
  4749. {
  4750.   cm_chapheading ();
  4751. }
  4752.  
  4753. void
  4754. cm_chapheading ()
  4755. {
  4756.   cm_chapter ();
  4757. }
  4758.  
  4759. void
  4760. cm_heading ()
  4761. {
  4762.   cm_section ();
  4763. }
  4764.  
  4765. void
  4766. cm_subheading ()
  4767. {
  4768.   cm_subsection ();
  4769. }
  4770.  
  4771. void
  4772. cm_subsubheading ()
  4773. {
  4774.   cm_subsubsection ();
  4775. }
  4776.  
  4777.  
  4778. /* **************************************************************** */
  4779. /*                                    */
  4780. /*           Adding nodes, and making tags            */
  4781. /*                                    */
  4782. /* **************************************************************** */
  4783.  
  4784. /* Start a new tag table. */
  4785. void
  4786. init_tag_table ()
  4787. {
  4788.   while (tag_table != (TAG_ENTRY *) NULL)
  4789.     {
  4790.       TAG_ENTRY *temp = tag_table;
  4791.       free (temp->node);
  4792.       free (temp->prev);
  4793.       free (temp->next);
  4794.       free (temp->up);
  4795.       tag_table = tag_table->next_ent;
  4796.       free (temp);
  4797.     }
  4798. }
  4799.  
  4800. void
  4801. write_tag_table ()
  4802. {
  4803.   write_tag_table_internal (0);    /* Not indirect. */
  4804. }
  4805.  
  4806. void
  4807. write_tag_table_indirect ()
  4808. {
  4809.   write_tag_table_internal (1);
  4810. }
  4811.  
  4812. #define TAG_TABLE_END_STRING "\037\nEnd Tag Table\n"
  4813.  
  4814. /* Write out the contents of the existing tag table.
  4815.    INDIRECT_P says how to format the output. */
  4816. void
  4817. write_tag_table_internal (indirect_p)
  4818.      int indirect_p;
  4819. {
  4820.   TAG_ENTRY *node = tag_table;
  4821.   int old_indent = no_indent;
  4822.  
  4823.   no_indent = 1;
  4824.   filling_enabled = 0;
  4825.   must_start_paragraph = 0;
  4826.   close_paragraph ();
  4827.  
  4828.   if (!indirect_p)
  4829.     {
  4830.       no_indent = 1;
  4831.       insert ('\n');
  4832.     }
  4833.  
  4834.   add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
  4835.  
  4836.   while (node != (TAG_ENTRY *) NULL)
  4837.     {
  4838.       add_word_args ("Node: %s\177%lu\n", node->node, node->position);
  4839.       node = node->next_ent;
  4840.     }
  4841.  
  4842.   add_word (TAG_TABLE_END_STRING);
  4843.   flush_output ();
  4844.   no_indent = old_indent;
  4845. }
  4846.  
  4847. char *
  4848. get_node_token ()
  4849. {
  4850.   char *string;
  4851.  
  4852.   get_until_in_line (",", &string);
  4853.  
  4854.   if (curchar () == ',')
  4855.     input_text_offset++;
  4856.  
  4857.   canon_white (string);
  4858.  
  4859.   /* Allow things like @@nodename. */
  4860.   normalize_node_name (string);
  4861.  
  4862.   return (string);
  4863. }
  4864.  
  4865. /* Given a node name in STRING, remove double @ signs, replacing them
  4866.    with just one.  Convert "top" and friends into "Top". */
  4867. void
  4868. normalize_node_name (string)
  4869.      char *string;
  4870. {
  4871.   register int i, l = strlen (string);
  4872.  
  4873.   for (i = 0; i < l; i++)
  4874.     {
  4875.       if (string[i] == '@' && string[i + 1] == '@')
  4876.     {
  4877.       strncpy (string + i, string + i + 1, l - i);
  4878.       l--;
  4879.     }
  4880.     }
  4881.   if (stricmp (string, "Top") == 0)
  4882.     strcpy (string, "Top");
  4883. }
  4884.  
  4885. /* Look up NAME in the tag table, and return the associated
  4886.    tag_entry.  If the node is not in the table return NULL. */
  4887. TAG_ENTRY *
  4888. find_node (name)
  4889.      char *name;
  4890. {
  4891.   TAG_ENTRY *tag = tag_table;
  4892.  
  4893.   while (tag != (TAG_ENTRY *) NULL)
  4894.     {
  4895.       if (strcmp (tag->node, name) == 0)
  4896.     return (tag);
  4897.       tag = tag->next_ent;
  4898.     }
  4899.   return ((TAG_ENTRY *) NULL);
  4900. }
  4901.  
  4902. /* Remember NODE and associates. */
  4903. void
  4904. remember_node (node, prev, next, up, position, line_no, no_warn)
  4905.      char *node, *prev, *next, *up;
  4906.      size_t position, line_no;
  4907.      int no_warn;
  4908. {
  4909.   /* Check for existence of this tag already. */
  4910.   if (validating)
  4911.     {
  4912.       register TAG_ENTRY *tag = find_node (node);
  4913.       if (tag)
  4914.     {
  4915.       line_error ("Node `%s' multiply defined (%lu is first definition)",
  4916.               node, tag->line_no);
  4917.       return;
  4918.     }
  4919.     }
  4920.  
  4921.   /* First, make this the current node. */
  4922.   current_node = node;
  4923.  
  4924.   /* Now add it to the list. */
  4925.   {
  4926.     TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
  4927.     new->node = node;
  4928.     new->prev = prev;
  4929.     new->next = next;
  4930.     new->up = up;
  4931.     new->position = position;
  4932.     new->line_no = line_no;
  4933.     new->filename = node_filename;
  4934.     new->touched = 0;        /* not yet referenced. */
  4935.     new->flags = 0;
  4936.     if (no_warn)
  4937.       new->flags |= NO_WARN;
  4938.     new->next_ent = tag_table;
  4939.     tag_table = new;
  4940.   }
  4941. }
  4942.  
  4943. /* The order is: nodename, nextnode, prevnode, upnode.
  4944.    If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
  4945.    You must follow a node command which has those fields defaulted
  4946.    with a sectioning command (e.g. @chapter) giving the "level" of that node.
  4947.    It is an error not to do so.
  4948.    The defaults come from the menu in this nodes parent. */
  4949. void
  4950. cm_node ()
  4951. {
  4952.   char *node, *prev, *next, *up;
  4953.   size_t new_node_pos;
  4954.   int defaulting, this_section, no_warn = 0;
  4955.   extern int already_outputting_pending_notes;
  4956.  
  4957.   if (strcmp (command, "nwnode") == 0)
  4958.     no_warn = 1;
  4959.  
  4960.   /* Get rid of unmatched brace arguments from previous commands. */
  4961.   discard_braces ();
  4962.  
  4963.   /* There also might be insertions left lying around that haven't been
  4964.      ended yet.  Do that also. */
  4965.   discard_insertions ();
  4966.  
  4967.   if (!already_outputting_pending_notes)
  4968.     {
  4969.       close_paragraph ();
  4970.       output_pending_notes ();
  4971.       free_pending_notes ();
  4972.     }
  4973.  
  4974.   filling_enabled = indented_fill = 0;
  4975.   new_node_pos = output_position;
  4976.   current_footnote_number = 1;
  4977.  
  4978.   node = get_node_token ();
  4979.   next = get_node_token ();
  4980.   prev = get_node_token ();
  4981.   up = get_node_token ();
  4982.  
  4983.   no_indent = 1;
  4984.   if (!no_headers)
  4985.     add_word_args ("\037\nFile: %s,  Node: %s", pretty_output_filename, node);
  4986.  
  4987.   /* Check for defaulting of this node's next, prev, and up fields. */
  4988.   defaulting = ((strlen (next) == 0) &&
  4989.         (strlen (prev) == 0) &&
  4990.         (strlen (up) == 0));
  4991.  
  4992.   this_section = what_section (input_text + input_text_offset);
  4993.  
  4994.   /* If we are defaulting, then look at the immediately following
  4995.      sectioning command (error if none) to determine the node's
  4996.      level.  Find the node that contains the menu mentioning this node
  4997.      that is one level up (error if not found).  That node is the "Up"
  4998.      of this node.  Default the "Next" and "Prev" from the menu. */
  4999.   if (defaulting)
  5000.     {
  5001.       NODE_REF *last_ref = (NODE_REF *)NULL;
  5002.       NODE_REF *ref = node_references;
  5003.  
  5004.       if (this_section < 0)
  5005.     {
  5006.       const char *polite_section_name = "top";
  5007.       int i;
  5008.  
  5009.       for (i = 0; section_alist[i].name; i++)
  5010.         if (section_alist[i].level == current_section + 1)
  5011.           {
  5012.         polite_section_name = section_alist[i].name;
  5013.         break;
  5014.           }
  5015.  
  5016.       line_error
  5017.         ("Node `%s' requires a sectioning command (e.g. @%s)",
  5018.          node, polite_section_name);
  5019.     }
  5020.       else
  5021.     {
  5022.       if (stricmp (node, "Top") == 0)
  5023.         {
  5024.           /* Default the NEXT pointer to be the first menu item in
  5025.          this node, if there is a menu in this node. */
  5026.           {
  5027.         size_t orig_offset, orig_size;
  5028.         /* char *glean_node_from_menu (); */
  5029.  
  5030.         orig_offset = input_text_offset;
  5031.         orig_size = search_forward ("\n@node ", orig_offset);
  5032.  
  5033.         if ((long) orig_size < 0)
  5034.           orig_size = size_of_input_text;
  5035.  
  5036.         input_text_offset = search_forward ("\n@menu", orig_offset);
  5037.         if ((long) input_text_offset > -1)
  5038.           {
  5039.             char *nodename_from_menu = (char *)NULL;
  5040.  
  5041.             input_text_offset =
  5042.               search_forward ("\n* ", input_text_offset);
  5043.  
  5044.             if ((long) input_text_offset != -1)
  5045.               nodename_from_menu = glean_node_from_menu (0);
  5046.  
  5047.             if (nodename_from_menu)
  5048.               {
  5049.             free (next);
  5050.             next = nodename_from_menu;
  5051.             prev = savestring ("(DIR)");
  5052.             up = savestring ("(DIR)");
  5053.               }
  5054.           }
  5055.         input_text_offset = orig_offset;
  5056.           }
  5057.         }
  5058.  
  5059.       while (ref)
  5060.         {
  5061.           if (ref->section == (this_section - 1) &&
  5062.           ref->type == menu_reference &&
  5063.           strcmp (ref->node, node) == 0)
  5064.         {
  5065.           char *containing_node = ref->containing_node;
  5066.  
  5067.           free (up);
  5068.           up = savestring (containing_node);
  5069.  
  5070.           if (last_ref &&
  5071.               last_ref->type == menu_reference &&
  5072.               (strcmp (last_ref->containing_node,
  5073.                    containing_node) == 0))
  5074.             {
  5075.               free (next);
  5076.               next = savestring (last_ref->node);
  5077.             }
  5078.  
  5079.           while ((ref->section == this_section - 1) &&
  5080.              (ref->next) &&
  5081.              (ref->next->type != menu_reference))
  5082.             ref = ref->next;
  5083.  
  5084.           if (ref->next && ref->type == menu_reference &&
  5085.               (strcmp (ref->next->containing_node,
  5086.                    containing_node) == 0))
  5087.             {
  5088.               free (prev);
  5089.               prev = savestring (ref->next->node);
  5090.             }
  5091.           else if (!ref->next &&
  5092.                stricmp (ref->containing_node, "Top") == 0)
  5093.             {
  5094.               free (prev);
  5095.               prev = savestring (ref->containing_node);
  5096.             }
  5097.           break;
  5098.         }
  5099.           last_ref = ref;
  5100.           ref = ref->next;
  5101.         }
  5102.     }
  5103.     }
  5104.  
  5105.   if (!no_headers)
  5106.     {
  5107.       if (*next)
  5108.     add_word_args (",  Next: %s", next);
  5109.  
  5110.       if (*prev)
  5111.     add_word_args (",  Prev: %s", prev);
  5112.  
  5113.       if (*up)
  5114.     add_word_args (",  Up: %s", up);
  5115.     }
  5116.  
  5117.   close_paragraph ();
  5118.   no_indent = 0;
  5119.  
  5120.   if (!*node)
  5121.     {
  5122.       line_error ("No node name specified for `@%s' command", command);
  5123.       free (node);
  5124.       free (next);
  5125.       free (prev);
  5126.       free (up);
  5127.     }
  5128.   else
  5129.     {
  5130.       if (!*next) { free (next); next = (char *)NULL; }
  5131.       if (!*prev) { free (prev); prev = (char *)NULL; }
  5132.       if (!*up) { free (up); up = (char *)NULL; }
  5133.       remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
  5134.     }
  5135.  
  5136.   /* Change the section only if there was a sectioning command. */
  5137.   if (this_section >= 0)
  5138.     current_section = this_section;
  5139.  
  5140.   filling_enabled = 1;
  5141. }
  5142.  
  5143. /* Validation of an info file.
  5144.    Scan through the list of tag entrys touching the Prev, Next, and Up
  5145.    elements of each.  It is an error not to be able to touch one of them,
  5146.    except in the case of external node references, such as "(DIR)".
  5147.  
  5148.    If the Prev is different from the Up,
  5149.    then the Prev node must have a Next pointing at this node.
  5150.  
  5151.    Every node except Top must have an Up.
  5152.    The Up node must contain some sort of reference, other than a Next,
  5153.    to this node.
  5154.  
  5155.    If the Next is different from the Next of the Up,
  5156.    then the Next node must have a Prev pointing at this node. */
  5157. void
  5158. validate_file (filename, tag_table)
  5159.      char *filename;
  5160.      TAG_ENTRY *tag_table;
  5161. {
  5162.   char *old_input_filename = input_filename;
  5163.   TAG_ENTRY *tags = tag_table;
  5164.  
  5165.   while (tags != (TAG_ENTRY *) NULL)
  5166.     {
  5167.       register TAG_ENTRY *temp_tag;
  5168.  
  5169.       input_filename = tags->filename;
  5170.       line_number = tags->line_no;
  5171.  
  5172.       /* If this is a "no warn" node, don't validate it in any way. */
  5173.       if (tags->flags & NO_WARN)
  5174.     {
  5175.       tags = tags->next_ent;
  5176.       continue;
  5177.     }
  5178.     
  5179.       /* If this node has a Next, then make sure that the Next exists. */
  5180.       if (tags->next)
  5181.     {
  5182.       (void) validate (tags->next, tags->line_no, "Next");
  5183.  
  5184.       /* If the Next node exists, and there is no Up, then make
  5185.          sure that the Prev of the Next points back. */
  5186.       if ((temp_tag = find_node (tags->next)))
  5187.         {
  5188.           char *prev;
  5189.  
  5190.           if (temp_tag->flags & NO_WARN)
  5191.         {
  5192.           /* Do nothing if we aren't supposed to issue warnings
  5193.              about this node. */
  5194.         }
  5195.           else
  5196.         {
  5197.           prev = temp_tag->prev;
  5198.           if (!prev || (strcmp (prev, tags->node) != 0))
  5199.             {
  5200.               line_error ("Node `%s''s Next field not pointed back to",
  5201.                   tags->node);
  5202.               line_number = temp_tag->line_no;
  5203.               input_filename = temp_tag->filename;
  5204.               line_error
  5205.             ("This node (`%s') is the one with the bad `Prev'",
  5206.              temp_tag->node);
  5207.               input_filename = tags->filename;
  5208.               line_number = tags->line_no;
  5209.               temp_tag->flags |= PREV_ERROR;
  5210.             }
  5211.         }
  5212.         }
  5213.     }
  5214.  
  5215.       /* Validate the Prev field if there is one, and we haven't already
  5216.      complained about it in some way.  You don't have to have a Prev
  5217.      field at this stage. */
  5218.       if (!(tags->flags & PREV_ERROR) && tags->prev)
  5219.     {
  5220.       int valid = validate (tags->prev, tags->line_no, "Prev");
  5221.  
  5222.       if (!valid)
  5223.         tags->flags |= PREV_ERROR;
  5224.       else
  5225.         {
  5226.           /* If the Prev field is not the same as the Up field,
  5227.          then the node pointed to by the Prev field must have
  5228.          a Next field which points to this node. */
  5229.           if (tags->up && (strcmp (tags->prev, tags->up) != 0))
  5230.         {
  5231.           temp_tag = find_node (tags->prev);
  5232.  
  5233.           /* If we aren't supposed to issue warnings about the
  5234.              target node, do nothing. */
  5235.           if (!temp_tag || (temp_tag->flags & NO_WARN))
  5236.             {
  5237.               /* Do nothing. */
  5238.             }
  5239.           else
  5240.             {
  5241.               if (!temp_tag->next ||
  5242.               (strcmp (temp_tag->next, tags->node) != 0))
  5243.             {
  5244.               line_error
  5245.                 ("Node `%s''s Prev field not pointed back to",
  5246.                  tags->node);
  5247.               line_number = temp_tag->line_no;
  5248.               input_filename = temp_tag->filename;
  5249.               line_error
  5250.                 ("This node (`%s') is the one with the bad `Next'",
  5251.                  temp_tag->node);
  5252.               input_filename = tags->filename;
  5253.               line_number = tags->line_no;
  5254.               temp_tag->flags |= NEXT_ERROR;
  5255.             }
  5256.             }
  5257.         }
  5258.         }
  5259.     }
  5260.  
  5261.       if (!tags->up && (stricmp (tags->node, "Top") != 0))
  5262.     line_error ("Node `%s' is missing an \"Up\" field", tags->node);
  5263.       else if (tags->up)
  5264.     {
  5265.       int valid = validate (tags->up, tags->line_no, "Up");
  5266.  
  5267.       /* If node X has Up: Y, then warn if Y fails to have a menu item
  5268.          or note pointing at X, if Y isn't of the form "(Y)". */
  5269.       if (valid && *tags->up != '(')
  5270.         {
  5271.           NODE_REF *nref, *tref, *list;
  5272.           /* NODE_REF *find_node_reference (); */
  5273.  
  5274.           tref = (NODE_REF *) NULL;
  5275.           list = node_references;
  5276.  
  5277.           for (;;)
  5278.         {
  5279.           if (!(nref = find_node_reference (tags->node, list)))
  5280.             break;
  5281.  
  5282.           if (strcmp (nref->containing_node, tags->up) == 0)
  5283.             {
  5284.               if (nref->type != menu_reference)
  5285.             {
  5286.               tref = nref;
  5287.               list = nref->next;
  5288.             }
  5289.               else
  5290.             break;
  5291.             }
  5292.           list = nref->next;
  5293.         }
  5294.  
  5295.           if (!nref)
  5296.         {
  5297.           temp_tag = find_node (tags->up);
  5298.           line_number = temp_tag->line_no;
  5299.           filename = temp_tag->filename;
  5300.           if (!tref)
  5301.             line_error (
  5302. "`%s' has an Up field of `%s', but `%s' has no menu item for `%s'",
  5303.                 tags->node, tags->up, tags->up, tags->node);
  5304.           line_number = tags->line_no;
  5305.           filename = tags->filename;
  5306.         }
  5307.         }
  5308.     }
  5309.       tags = tags->next_ent;
  5310.     }
  5311.  
  5312.   validate_other_references (node_references);
  5313.   /* We have told the user about the references which didn't exist.
  5314.      Now tell him about the nodes which aren't referenced. */
  5315.  
  5316.   tags = tag_table;
  5317.   while (tags != (TAG_ENTRY *) NULL)
  5318.     {
  5319.       /* If this node is a "no warn" node, do nothing. */
  5320.       if (tags->flags & NO_WARN)
  5321.     {
  5322.       tags = tags->next_ent;
  5323.       continue;
  5324.     }
  5325.  
  5326.       /* Special hack.  If the node in question appears to have
  5327.          been referenced more than REFERENCE_WARNING_LIMIT times,
  5328.          give a warning. */
  5329.       if (tags->touched > reference_warning_limit)
  5330.     {
  5331.       input_filename = tags->filename;
  5332.       line_number = tags->line_no;
  5333.       warning ("Node `%s' has been referenced %d times",
  5334.            tags->node, tags->touched);
  5335.     }
  5336.  
  5337.       if (tags->touched == 0)
  5338.     {
  5339.       input_filename = tags->filename;
  5340.       line_number = tags->line_no;
  5341.  
  5342.       /* Notice that the node "Top" is special, and doesn't have to
  5343.          be referenced. */
  5344.       if (stricmp (tags->node, "Top") != 0)
  5345.         warning ("Unreferenced node `%s'", tags->node);
  5346.     }
  5347.       tags = tags->next_ent;
  5348.     }
  5349.   input_filename = old_input_filename;
  5350. }
  5351.  
  5352. /* Return 1 if tag correctly validated, or 0 if not. */
  5353. int
  5354. validate (tag, line, label)
  5355.      char *tag;
  5356.      size_t line;
  5357.      const char *label;
  5358. {
  5359.   TAG_ENTRY *result;
  5360.  
  5361.   /* If there isn't a tag to verify, or if the tag is in another file,
  5362.      then it must be okay. */
  5363.   if (!tag || !*tag || *tag == '(')
  5364.     return (1);
  5365.  
  5366.   /* Otherwise, the tag must exist. */
  5367.   result = find_node (tag);
  5368.  
  5369.   if (!result)
  5370.     {
  5371.       line_number = line;
  5372.       line_error (
  5373. "Validation error.  `%s' field points to node `%s', which doesn't exist",
  5374.           label, tag);
  5375.       return (0);
  5376.     }
  5377.   result->touched++;
  5378.   return (1);
  5379. }
  5380.  
  5381. /* Split large output files into a series of smaller files.  Each file
  5382.    is pointed to in the tag table, which then gets written out as the
  5383.    original file.  The new files have the same name as the original file
  5384.    with a "-num" attached.  SIZE is the largest number of bytes to allow
  5385.    in any single split file. */
  5386. /* For a file system with 8+3 names will usurp the whole extension for
  5387.    file number, in a form of .-num so we may have up to 99 files */
  5388. void
  5389. split_file (filename, size)
  5390.      char *filename;
  5391.      size_t size;
  5392. {
  5393.   char *root_filename, *root_pathname;
  5394.   char *the_file; /* , *filename_part (); */
  5395.   struct stat fileinfo;
  5396.   char *the_header;
  5397.   size_t header_size;
  5398.  
  5399.   if (size == 0)
  5400.     size = DEFAULT_SPLIT_SIZE;
  5401.  
  5402.   if ((stat (filename, &fileinfo) != 0) ||
  5403.       (fileinfo.st_size < SPLIT_SIZE_THRESHOLD))
  5404.     return;
  5405.  
  5406.   the_file = find_and_load (filename);
  5407.   /* find_and_load() sets the_file to input_text */
  5408.   if (!the_file)
  5409.     return;
  5410.  
  5411.   /* Can only do this to files with tag tables. */
  5412.   if (NULL == (tag_table = restore_tag_table(the_file, size_of_input_text)))
  5413.       return;  /* no tag table */
  5414.  
  5415.   root_filename = filename_part (filename);
  5416. #ifdef  atarist
  5417.   /* truncate file name extension */
  5418.   {
  5419.       char *loc;
  5420.       if (NULL == (loc = rindex (root_filename, '.'))) {
  5421.       loc = root_filename + strlen(root_filename);
  5422.       *loc = '.';
  5423.       }
  5424.       *(loc + 1) = '\0';
  5425.   }
  5426. #endif /* atarist */
  5427.   root_pathname = pathname_part (filename);
  5428.  
  5429.   if (!root_pathname)
  5430.     root_pathname = savestring ("");
  5431.  
  5432.   /* Start splitting the file.  Walk along the tag table
  5433.      outputting sections of the file.  When we have written
  5434.      all of the nodes in the tag table, make the top-level
  5435.      pointer file, which contains indirect pointers and
  5436.      tags for the nodes. */
  5437.   {
  5438.     int which_file = 1;
  5439.     TAG_ENTRY *tags = tag_table;
  5440.     char *indirect_info = (char *)NULL;
  5441.  
  5442.     /* Remember the `header' of this file.  The first tag in the file is
  5443.        the bottom of the header; the top of the file is the start. */
  5444.     /*
  5445.     the_header = (char *)xmalloc (1 + (header_size = tags->position));
  5446.     memcpy (the_header, the_file, header_size);
  5447.     */
  5448.     /* We are only writing this out, so we do need really a copy */
  5449.     the_header = the_file;
  5450.     header_size = (tags->position);
  5451.  
  5452.     while (tags)
  5453.       {
  5454.     size_t file_top, file_bot, limit;
  5455. #ifdef DOTS
  5456.     fprintf(stderr, ".");        /* we are still crunching */
  5457. #endif
  5458.  
  5459.     /* Have to include the Control-_. */
  5460.     file_top = file_bot = tags->position;
  5461.     limit = file_top + size;
  5462.  
  5463.     /* If the rest of this file is only one node, then
  5464.        that is the entire subfile. */
  5465.     if (!tags->next_ent)
  5466.       {
  5467.         size_t i = tags->position + 1;
  5468.         char last_char = the_file[i];
  5469.  
  5470.         while (i < size_of_input_text)
  5471.           {
  5472.         if ((the_file[i] == '\037') &&
  5473.             ((last_char == '\n') ||
  5474.              (last_char == '\014')))
  5475.           break;
  5476.         else
  5477.           last_char = the_file[i];
  5478.         i++;
  5479.           }
  5480.         file_bot = i;
  5481.         tags = tags->next_ent;
  5482.         goto write_region;
  5483.       }
  5484.  
  5485.     /* Otherwise, find the largest number of nodes that can fit in
  5486.        this subfile. */
  5487.     for (; tags; tags = tags->next_ent)
  5488.       {
  5489.         if (!tags->next_ent)
  5490.           {
  5491.         /* This entry is the last node.  Search forward for the end
  5492.                of this node, and that is the end of this file. */
  5493.         size_t i = tags->position + 1;
  5494.         char last_char = the_file[i];
  5495.  
  5496.         while (i < size_of_input_text)
  5497.           {
  5498.             if ((the_file[i] == '\037') &&
  5499.             ((last_char == '\n') ||
  5500.              (last_char == '\014')))
  5501.               break;
  5502.             else
  5503.               last_char = the_file[i];
  5504.             i++;
  5505.           }
  5506.         file_bot = i;
  5507.  
  5508.         if (file_bot < limit)
  5509.           {
  5510.             tags = tags->next_ent;
  5511.             goto write_region;
  5512.           }
  5513.         else
  5514.           {
  5515.             /* Here we want to write out everything before the last
  5516.                node, and then write the last node out in a file
  5517.                by itself. */
  5518.             file_bot = tags->position;
  5519.             goto write_region;
  5520.           }
  5521.           }
  5522.  
  5523.         if (tags->next_ent->position > limit)
  5524.           {
  5525.         if (tags->position == file_top)
  5526.           tags = tags->next_ent;
  5527.  
  5528.         file_bot = tags->position;
  5529.  
  5530.           write_region:
  5531.         {
  5532.           char *split_filename;
  5533.  
  5534.           split_filename = (char *) xmalloc
  5535.             (10 + strlen (root_pathname) + strlen (root_filename));
  5536.           sprintf
  5537.             (split_filename,
  5538.              "%s%s-%d", root_pathname, root_filename, which_file);
  5539.  
  5540.           output_filename = split_filename; /* flush_output needs it */
  5541. #ifndef atarist
  5542.             {
  5543.           int fd;
  5544.           fd = open
  5545.             (split_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
  5546.  
  5547.           if ((fd < 0) ||
  5548.               (write (fd, the_header, header_size) != header_size) ||
  5549.               (write (fd, the_file + file_top, file_bot - file_top)
  5550.                != (file_bot - file_top)) ||
  5551.               ((close (fd)) < 0))
  5552.             {
  5553.               perror (split_filename);
  5554.               if (fd != -1)
  5555.             close (fd);
  5556.               exit (FATAL);
  5557.             }
  5558.             }
  5559. #else /* atarist */
  5560.           /*
  5561.            * we cannot just 'write' due to differences between
  5562.            * text and binary files - we will use flush_output()
  5563.            * instead; it also checks for errors on write
  5564.            */
  5565.           if (NULL !=
  5566.               (output_stream = fopen (split_filename, WMODE))) {
  5567.             output_paragraph = the_header;
  5568.             output_paragraph_offset = header_size;
  5569.             flush_output();
  5570.             output_paragraph = the_file + file_top;
  5571.             output_paragraph_offset = file_bot - file_top;
  5572.             flush_output();
  5573.             fclose (output_stream);
  5574.           }
  5575. #endif /* atarist */
  5576.   
  5577.           if (!indirect_info)
  5578.             {
  5579.               indirect_info = the_file + file_top;
  5580. /*
  5581.               sprintf (indirect_info, "\037\nIndirect:\n");
  5582.               indirect_info += strlen (indirect_info);
  5583. *//* sprintf returns a number of character written anyway */
  5584.               indirect_info +=
  5585.             sprintf (indirect_info, "\037\nIndirect:\n");
  5586.             }
  5587.  
  5588. #ifndef atarist
  5589.           indirect_info +=
  5590.             sprintf (indirect_info, "%s-%d: %lu\n",
  5591.                  root_filename, which_file, file_top);
  5592. #else  /* atarist */
  5593.           indirect_info +=
  5594.             sprintf (indirect_info, "%sinfo-%d: %lu\n",
  5595.                  root_filename, which_file, file_top);
  5596. #endif /* atarist */
  5597.           free (split_filename);
  5598.           /* indirect_info += strlen (indirect_info); */
  5599.           which_file++;
  5600.           break;
  5601.         }
  5602.           }
  5603.       }
  5604.       }
  5605.  
  5606.     /* We have sucessfully created the subfiles.  Now write out the
  5607.        original again.  We must use `output_stream', or
  5608.        write_tag_table_indirect () won't know where to place the output. */
  5609.     output_stream = fopen (filename, WMODE);
  5610.     if (!output_stream)
  5611.       {
  5612.     perror (filename);
  5613.     exit (FATAL);
  5614.       }
  5615.  
  5616.     {
  5617.       size_t distance = indirect_info - the_file;
  5618.       fwrite (the_file, 1, distance, output_stream);
  5619.  
  5620.       /* make the_file into paragraph_output_buffer - the contents
  5621.      of this buffer is already written out so we may reuse it */
  5622.       output_paragraph = the_file;
  5623.       paragraph_buffer_len = size_of_input_text;
  5624.  
  5625.       /* Set initial parameters and Inhibit newlines. */
  5626.       init_par_parameters ();
  5627.  
  5628.       write_tag_table_indirect ();
  5629.       fclose (output_stream);
  5630. /*      free (the_header);  */
  5631.       free (the_file);
  5632.       return;
  5633.     }
  5634.   }
  5635. }
  5636.  
  5637. /* Some menu hacking.  This is used to remember menu references while
  5638.    reading the input file.  After the output file has been written, if
  5639.    validation is on, then we use the contents of NODE_REFERENCES as a
  5640.    list of nodes to validate. */
  5641. const char *
  5642. reftype_type_string (type)
  5643.      enum reftype type;
  5644. {
  5645.   switch (type)
  5646.     {
  5647.     case menu_reference:
  5648.       return ("Menu");
  5649.     case followed_reference:
  5650.       return ("Followed-Reference");
  5651.     default:
  5652.       return ("Internal-bad-reference-type");
  5653.     }
  5654. }
  5655.  
  5656. /* Remember this node name for later validation use. */
  5657. void
  5658. remember_node_reference (node, line, type)
  5659.      char *node;
  5660.      size_t line;
  5661.      enum reftype type;
  5662. {
  5663.   NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF));
  5664.  
  5665.   temp->next = node_references;
  5666.   temp->node = savestring (node);
  5667.   temp->line_no = line;
  5668.   temp->section = current_section;
  5669.   temp->type = type;
  5670.   temp->containing_node = savestring (current_node);
  5671.   temp->filename = node_filename;
  5672.  
  5673.   node_references = temp;
  5674. }
  5675.  
  5676. void
  5677. validate_other_references (ref_list)
  5678.      register NODE_REF *ref_list;
  5679. {
  5680.   char *old_input_filename = input_filename;
  5681.  
  5682.   while (ref_list != (NODE_REF *) NULL)
  5683.     {
  5684.       input_filename = ref_list->filename;
  5685.       validate (ref_list->node, ref_list->line_no,
  5686.         reftype_type_string (ref_list->type));
  5687.       ref_list = ref_list->next;
  5688.     }
  5689.   input_filename = old_input_filename;
  5690. }
  5691.  
  5692. /* Find NODE in REF_LIST. */
  5693. NODE_REF *
  5694. find_node_reference (node, ref_list)
  5695.      char *node;
  5696.      register NODE_REF *ref_list;
  5697. {
  5698.   while (ref_list)
  5699.     {
  5700.       if (strcmp (node, ref_list->node) == 0)
  5701.     break;
  5702.       ref_list = ref_list->next;
  5703.     }
  5704.   return (ref_list);
  5705. }
  5706.  
  5707. void
  5708. free_node_references ()
  5709. {
  5710.   register NODE_REF *list, *temp;
  5711.  
  5712.   list = node_references;
  5713.  
  5714.   while (list)
  5715.     {
  5716.       temp = list;
  5717.       free (list->node);
  5718.       free (list->containing_node);
  5719.       list = list->next;
  5720.       free (temp);
  5721.     }
  5722.   node_references = (NODE_REF *) NULL;
  5723. }
  5724.  
  5725.   /* This function gets called at the start of every line while inside of
  5726.      a menu.  It checks to see if the line starts with "* ", and if so,
  5727.      remembers the node reference that this menu refers to.
  5728.      input_text_offset is at the \n just before the line start. */
  5729. #define menu_starter "* "
  5730. char *
  5731. glean_node_from_menu (remember_reference)
  5732.      int remember_reference;
  5733. {
  5734.   size_t i, orig_offset = input_text_offset;
  5735.   char *nodename;
  5736.  
  5737.   if (strncmp (&input_text[input_text_offset + 1],
  5738.            menu_starter,
  5739.            strlen (menu_starter)) != 0)
  5740.     return ((char *)NULL);
  5741.   else
  5742.     input_text_offset += strlen (menu_starter) + 1;
  5743.  
  5744.   get_until_in_line (":", &nodename);
  5745.   if (curchar () == ':')
  5746.     input_text_offset++;
  5747.   canon_white (nodename);
  5748.  
  5749.   if (curchar () == ':')
  5750.     goto save_node;
  5751.  
  5752.   free (nodename);
  5753.   get_rest_of_line (&nodename);
  5754.  
  5755.   /* Special hack: If the nodename follows the menu item name,
  5756.      then we have to read the rest of the line in order to find
  5757.      out what the nodename is.  But we still have to read the
  5758.      line later, in order to process any formatting commands that
  5759.      might be present.  So un-count the carriage return that has just
  5760.      been counted. */
  5761.   line_number--;
  5762.  
  5763.   isolate_nodename (nodename);
  5764.  
  5765. save_node:
  5766.   input_text_offset = orig_offset;
  5767.   normalize_node_name (nodename);
  5768.   i = strlen (nodename);
  5769.   if (i && nodename[i - 1] == ':')
  5770.     nodename[i - 1] = '\0';
  5771.  
  5772.   if (remember_reference)
  5773.     {
  5774.       remember_node_reference (nodename, line_number, menu_reference);
  5775.       free (nodename);
  5776.       return ((char *)NULL);
  5777.     }
  5778.   else
  5779.     return (nodename);
  5780. }
  5781.  
  5782. static void
  5783. isolate_nodename (nodename)
  5784.      char *nodename;
  5785. {
  5786.   register int i, c;
  5787.   int paren_seen, paren;
  5788.  
  5789.   if (!nodename)
  5790.     return;
  5791.  
  5792.   canon_white (nodename);
  5793.   paren_seen = paren = i = 0;
  5794.  
  5795.   if (*nodename == '.' || !*nodename)
  5796.     {
  5797.       *nodename = '\0';
  5798.       return;
  5799.     }
  5800.  
  5801.   if (*nodename == '(')
  5802.     {
  5803.       paren++;
  5804.       paren_seen++;
  5805.       i++;
  5806.     }
  5807.  
  5808.   for (; (c = nodename[i]); i++)
  5809.     {
  5810.       if (paren)
  5811.     {
  5812.       if (c == '(')
  5813.         paren++;
  5814.       else if (c == ')')
  5815.         paren--;
  5816.  
  5817.       continue;
  5818.     }
  5819.  
  5820.       /* If the character following the close paren is a space, then this
  5821.      node has no more characters associated with it. */
  5822.       if (c == '\t' ||
  5823.       c == '\n' ||
  5824.       c == ','  ||
  5825.       ((paren_seen && nodename[i - 1] == ')') &&
  5826.        (c == ' ' || c == '.')) ||
  5827.       (c == '.' &&
  5828.        ((!nodename[i + 1] ||
  5829.          (cr_or_whitespace (nodename[i + 1])) ||
  5830.          (nodename[i + 1] == ')')))))
  5831.     break;
  5832.     }
  5833.   nodename[i] = '\0';
  5834. }
  5835.  
  5836. void
  5837. cm_menu ()
  5838. {
  5839.   begin_insertion (menu);
  5840. }
  5841.  
  5842.  
  5843. /* **************************************************************** */
  5844. /*                                    */
  5845. /*            Cross Reference Hacking                */
  5846. /*                                    */
  5847. /* **************************************************************** */
  5848.  
  5849. char *
  5850. get_xref_token ()
  5851. {
  5852.   char *string;
  5853.  
  5854.   get_until_in_braces (",", &string);
  5855.   if (curchar () == ',')
  5856.     input_text_offset++;
  5857.   fix_whitespace (string);
  5858.   return (string);
  5859. }
  5860.  
  5861. int px_ref_flag = 0;        /* Controls initial output string. */
  5862.  
  5863. /* Make a cross reference. */
  5864. void
  5865. cm_xref (arg)
  5866. int arg;
  5867. {
  5868.   if (arg == START)
  5869.     {
  5870.       char *arg1, *arg2, *arg3, *arg4, *arg5;
  5871.  
  5872.       arg1 = get_xref_token ();
  5873.       arg2 = get_xref_token ();
  5874.       arg3 = get_xref_token ();
  5875.       arg4 = get_xref_token ();
  5876.       arg5 = get_xref_token ();
  5877.  
  5878.       add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
  5879.  
  5880.       if (*arg5 || *arg4)
  5881.     {
  5882.       char *node_name;
  5883.  
  5884.       if (!*arg2)
  5885.         {
  5886.           if (*arg3)
  5887.         node_name = arg3;
  5888.           else
  5889.         node_name = arg1;
  5890.         }
  5891.       else
  5892.         node_name = arg2;
  5893.  
  5894.       execute_string ("%s: (%s)%s", node_name, arg4, arg1);
  5895.       return;
  5896.     }
  5897.       else
  5898.     remember_node_reference (arg1, line_number, followed_reference);
  5899.  
  5900.       if (*arg3)
  5901.     {
  5902.       if (!*arg2)
  5903.         execute_string ("%s: %s", arg3, arg1);
  5904.       else
  5905.         execute_string ("%s: %s", arg2, arg1);
  5906.       return;
  5907.     }
  5908.  
  5909.       if (*arg2)
  5910.     execute_string ("%s: %s", arg2, arg1);
  5911.       else
  5912.     execute_string ("%s::", arg1);
  5913.     }
  5914.   else
  5915.     {
  5916.  
  5917.       /* Check to make sure that the next non-whitespace character is either
  5918.          a period or a comma. input_text_offset is pointing at the "}" which
  5919.          ended the xref or pxref command. */
  5920.       size_t temp = input_text_offset + 1;
  5921.  
  5922.       if (output_paragraph[output_paragraph_offset - 2] == ':' &&
  5923.       output_paragraph[output_paragraph_offset - 1] == ':')
  5924.     return;
  5925.       while (temp < size_of_input_text)
  5926.     {
  5927.       if (cr_or_whitespace (input_text[temp]))
  5928.         temp++;
  5929.       else
  5930.         {
  5931.           if (input_text[temp] == '.' ||
  5932.           input_text[temp] == ',' ||
  5933.           input_text[temp] == '\t')
  5934.         return;
  5935.           else
  5936.         {
  5937.           line_error (
  5938.         "Cross-reference must be terminated with a period or a comma");
  5939.           return;
  5940.         }
  5941.         }
  5942.     }
  5943.     }
  5944. }
  5945.  
  5946. void
  5947. cm_pxref (arg)
  5948.      int arg;
  5949. {
  5950.   if (arg == START)
  5951.     {
  5952.       px_ref_flag++;
  5953.       cm_xref (arg);
  5954.       px_ref_flag--;
  5955.     }
  5956.   else
  5957.     add_char ('.');
  5958. }
  5959.  
  5960. void
  5961. cm_inforef (arg)
  5962.      int arg;
  5963. {
  5964.   if (arg == START)
  5965.     {
  5966.       char *node, *pname, *file;
  5967.  
  5968.       node = get_xref_token ();
  5969.       pname = get_xref_token ();
  5970.       file = get_xref_token ();
  5971.  
  5972.       execute_string ("*note %s: (%s)%s", pname, file, node);
  5973.     }
  5974. }
  5975.  
  5976. /* **************************************************************** */
  5977. /*                                    */
  5978. /*            Insertion Command Stubs                */
  5979. /*                                    */
  5980. /* **************************************************************** */
  5981.  
  5982. void
  5983. cm_quotation ()
  5984. {
  5985.   begin_insertion (quotation);
  5986. }
  5987.  
  5988. void
  5989. cm_example ()
  5990. {
  5991.   begin_insertion (example);
  5992. }
  5993.  
  5994. void
  5995. cm_smallexample ()
  5996. {
  5997.   begin_insertion (smallexample);
  5998. }
  5999.  
  6000. void
  6001. cm_lisp ()
  6002. {
  6003.   begin_insertion (lisp);
  6004. }
  6005.  
  6006. void
  6007. cm_smalllisp ()
  6008. {
  6009.   begin_insertion (smalllisp);
  6010. }
  6011.  
  6012. /* @cartouche/@end cartouche draws box with rounded corners in
  6013.    TeX output.  Right now, just a NOP insertion. */
  6014. void
  6015. cm_cartouche ()
  6016. {
  6017.   begin_insertion (cartouche);
  6018. }
  6019.  
  6020. void
  6021. cm_format ()
  6022. {
  6023.   begin_insertion (format);
  6024. }
  6025.  
  6026. void
  6027. cm_display ()
  6028. {
  6029.   begin_insertion (display);
  6030. }
  6031.  
  6032. void
  6033. cm_itemize ()
  6034. {
  6035.   begin_insertion (itemize);
  6036. }
  6037.  
  6038. void
  6039. cm_enumerate ()
  6040. {
  6041.   do_enumeration (enumerate, "1");
  6042. }
  6043.  
  6044. /* Start an enumeration insertion of type TYPE.  If the user supplied
  6045.    no argument on the line, then use DEFAULT_STRING as the initial string. */
  6046. void
  6047. do_enumeration (type, default_string)
  6048.      int type;
  6049.      const char *default_string;
  6050. {
  6051.   get_until_in_line (".", &enumeration_arg);
  6052.   canon_white (enumeration_arg);
  6053.  
  6054.   if (!*enumeration_arg)
  6055.     {
  6056.       free (enumeration_arg);
  6057.       enumeration_arg = savestring (default_string);
  6058.     }
  6059.  
  6060.   if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg))
  6061.     {
  6062.       warning ("%s requires a letter or a digit", insertion_type_pname (type));
  6063.  
  6064.       switch (type)
  6065.     {
  6066.     case enumerate:
  6067.       default_string = "1";
  6068.       break;
  6069.     }
  6070.       enumeration_arg = savestring (default_string);
  6071.     }
  6072.   begin_insertion (type);
  6073. }
  6074.  
  6075. void
  6076. cm_table ()
  6077. {
  6078.   begin_insertion (table);
  6079. }
  6080.  
  6081. void
  6082. cm_ftable ()
  6083. {
  6084.   begin_insertion (ftable);
  6085. }
  6086.  
  6087. void
  6088. cm_vtable ()
  6089. {
  6090.   begin_insertion (vtable);
  6091. }
  6092.  
  6093. void
  6094. cm_group ()
  6095. {
  6096.   begin_insertion (group);
  6097. }
  6098.  
  6099. void
  6100. cm_ifinfo ()
  6101. {
  6102.   begin_insertion (ifinfo);
  6103. }
  6104.  
  6105. /* Begin an insertion where the lines are not filled or indented. */
  6106. void
  6107. cm_flushleft ()
  6108. {
  6109.   begin_insertion (flushleft);
  6110. }
  6111.  
  6112. /* Begin an insertion where the lines are not filled, and each line is
  6113.    forced to the right-hand side of the page. */
  6114. void
  6115. cm_flushright ()
  6116. {
  6117.   begin_insertion (flushright);
  6118. }
  6119.  
  6120.  
  6121. /* **************************************************************** */
  6122. /*                                    */
  6123. /*              Conditional Handling                */
  6124. /*                                    */
  6125. /* **************************************************************** */
  6126.  
  6127. /* A structure which contains `defined' variables. */
  6128. /** already defined
  6129. typedef struct _defines {
  6130.   struct _defines *next;
  6131.   char *name;
  6132.   char *value;
  6133. } DEFINE;
  6134. **/
  6135.  
  6136. /* The linked list of `set' defines. */
  6137. DEFINE *defines = (DEFINE *)NULL;
  6138.  
  6139. /* Add NAME to the list of `set' defines. */
  6140. void
  6141. set (name, value)
  6142.      char *name;
  6143.      char *value;
  6144. {
  6145.   DEFINE *temp;
  6146.  
  6147.   for (temp = defines; temp; temp = temp->next)
  6148.     if (strcmp (name, temp->name) == 0)
  6149.       {
  6150.     free (temp->value);
  6151.     temp->value = savestring (value);
  6152.     return;
  6153.       }
  6154.  
  6155.   temp = (DEFINE *)xmalloc (sizeof (DEFINE));
  6156.   temp->next = defines;
  6157.   temp->name = savestring (name);
  6158.   temp->value = savestring (value);
  6159.   defines = temp;
  6160. }
  6161.  
  6162. /* Remove NAME from the list of `set' defines. */
  6163. void
  6164. clear (name)
  6165.      char *name;
  6166. {
  6167.   register DEFINE *temp, *last;
  6168.  
  6169.   last = (DEFINE *)NULL;
  6170.   temp = defines;
  6171.  
  6172.   while (temp)
  6173.     {
  6174.       if (strcmp (temp->name, name) == 0)
  6175.     {
  6176.       if (last)
  6177.         last->next = temp->next;
  6178.       else
  6179.         defines = temp->next;
  6180.  
  6181.       free (temp->name);
  6182.       free (temp->value);
  6183.       free (temp);
  6184.       break;
  6185.     }
  6186.       last = temp;
  6187.       temp = temp->next;
  6188.     }
  6189. }
  6190.  
  6191. /* Return the value of NAME.  The return value is NULL if NAME is unset. */
  6192. char *
  6193. set_p (name)
  6194.      char *name;
  6195. {
  6196.   register DEFINE *temp;
  6197.  
  6198.   for (temp = defines; temp; temp = temp->next)
  6199.     if (strcmp (temp->name, name) == 0)
  6200.       return (temp->value);
  6201.  
  6202.   return ((char *)NULL);
  6203. }
  6204.  
  6205. /* Conditionally parse based on the current command name. */
  6206. void
  6207. command_name_condition ()
  6208. {
  6209.   char *discarder;
  6210.  
  6211.   discarder = (char *)xmalloc (8 + strlen (command));
  6212.  
  6213.   sprintf (discarder, "\n@end %s", command);
  6214.   discard_until (discarder);
  6215.   discard_until ("\n");
  6216.  
  6217.   free (discarder);
  6218. }
  6219.  
  6220. /* Create a variable whose name appears as the first word on this line. */
  6221. void
  6222. cm_set ()
  6223. {
  6224.   handle_variable (SET);
  6225. }
  6226.  
  6227. /* Remove a variable whose name appears as the first word on this line. */
  6228. void
  6229. cm_clear ()
  6230. {
  6231.   handle_variable (CLEAR);
  6232. }
  6233.  
  6234. void
  6235. cm_ifset ()
  6236. {
  6237.   handle_variable (IFSET);
  6238. }
  6239.  
  6240. void
  6241. cm_ifclear ()
  6242. {
  6243.   handle_variable (IFCLEAR);
  6244. }
  6245.  
  6246. void
  6247. cm_value (arg, start_pos, end_pos)
  6248.      int arg;
  6249.      size_t start_pos, end_pos;
  6250. {
  6251.   if (arg == END)
  6252.     {
  6253.       char *name, *value;
  6254.       name = (char *)&output_paragraph[start_pos];
  6255.       output_paragraph[end_pos] = '\0';
  6256.       name = savestring (name);
  6257.       value = set_p (name);
  6258.       output_column -= end_pos - start_pos;
  6259.       output_paragraph_offset = start_pos;
  6260.  
  6261.       if (value)
  6262.         execute_string ("%s", value);
  6263.       else
  6264.     add_word_args ("{No Value For \"%s\"}", name);
  6265.  
  6266.       free (name);
  6267.     }
  6268. }
  6269.  
  6270. /* Set, clear, or conditionalize based on ACTION. */
  6271. void
  6272. handle_variable (action)
  6273.      int action;
  6274. {
  6275.   char *name;
  6276.  
  6277.   get_rest_of_line (&name);
  6278.   backup_input_pointer ();
  6279.   canon_white (name);
  6280.   handle_variable_internal (action, name);
  6281.   free (name);
  6282. }
  6283.  
  6284. void
  6285. handle_variable_internal (action, name)
  6286.      int action;
  6287.      char *name;
  6288. {
  6289.   char *temp;
  6290.   int delimiter, additional_text_present = 0;
  6291.  
  6292.   /* Only the first word of NAME is a valid tag. */
  6293.   temp = name;
  6294.   delimiter = 0;
  6295.   while (*temp && (delimiter || !whitespace (*temp)))
  6296.     {
  6297. #if defined (SET_WITH_EQUAL)
  6298.       if (*temp == '"' || *temp == '\'')
  6299.     {
  6300.       if (*temp == delimiter)
  6301.         delimiter = 0;
  6302.       else
  6303.         delimiter = *temp;
  6304.     }
  6305. #endif /* SET_WITH_EQUAL */
  6306.       temp++;
  6307.     }
  6308.  
  6309.   if (*temp)
  6310.     additional_text_present++;
  6311.  
  6312.   *temp = '\0';
  6313.  
  6314.   if (!*name)
  6315.     line_error ("@%s requires a name", command);
  6316.   else
  6317.     {
  6318.       switch (action)
  6319.     {
  6320.     case SET:
  6321.       {
  6322.         char *value;
  6323.  
  6324. #if defined (SET_WITH_EQUAL)
  6325.         /* Allow a value to be saved along with a variable.  The value is
  6326.            the text following an `=' sign in NAME, if any is present. */
  6327.  
  6328.         for (value = name; *value && *value != '='; value++);
  6329.  
  6330.         if (*value)
  6331.           *value++ = '\0';
  6332.  
  6333.         if (*value == '"' || *value == '\'')
  6334.           {
  6335.         value++;
  6336.         value[strlen (value) - 1] = '\0';
  6337.           }
  6338.  
  6339. #else /* !SET_WITH_EQUAL */
  6340.         /* The VALUE of NAME is the remainder of the line sans
  6341.            whitespace. */
  6342.         if (additional_text_present)
  6343.           {
  6344.         value = temp + 1;
  6345.         canon_white (value);
  6346.           }
  6347.         else
  6348.           value = "";
  6349. #endif /* !SET_WITH_VALUE */
  6350.  
  6351.         set (name, value);
  6352.       }
  6353.       break;
  6354.  
  6355.     case CLEAR:
  6356.       clear (name);
  6357.       break;
  6358.  
  6359.     case IFSET:
  6360.     case IFCLEAR:
  6361.       /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
  6362.          read lines from the the file until we reach a matching
  6363.          "@end CONDITION".  This means that we only take note of
  6364.          "@ifset/clear" and "@end" commands. */
  6365.       {
  6366.         char condition[8];
  6367.         int condition_len;
  6368.  
  6369.         if (action == IFSET)
  6370.           strcpy (condition, "ifset");
  6371.         else
  6372.           strcpy (condition, "ifclear");
  6373.  
  6374.         condition_len = strlen (condition);
  6375.  
  6376.       if ((action == IFSET && !set_p (name)) ||
  6377.           (action == IFCLEAR && set_p (name)))
  6378.         {
  6379.           int level = 0, done = 0;
  6380.  
  6381.           while (!done)
  6382.         {
  6383.           char *freeable_line, *line;
  6384.  
  6385.           get_rest_of_line (&freeable_line);
  6386.  
  6387.           for (line = freeable_line; whitespace (*line); line++);
  6388.  
  6389.           if (*line == COMMAND_PREFIX &&
  6390.               (strncmp (line + 1, condition, condition_len) == 0))
  6391.             level++;
  6392.           else if (strncmp (line, "@end", 4) == 0)
  6393.             {
  6394.               char *cname = line + 4;
  6395.               char *temp;
  6396.  
  6397.               while (*cname && whitespace (*cname))
  6398.             cname++;
  6399.               temp = cname;
  6400.  
  6401.               while (*temp && !whitespace (*temp))
  6402.             temp++;
  6403.               *temp = '\0';
  6404.  
  6405.               if (strcmp (cname, condition) == 0)
  6406.             {
  6407.               if (!level)
  6408.                 {
  6409.                   done = 1;
  6410.                 }
  6411.               else
  6412.                 level--;
  6413.             }
  6414.             }
  6415.           free (freeable_line);
  6416.         }
  6417.           /* We found the end of a false @ifset/ifclear.  If we are
  6418.          in a menu, back up over the newline that ends the ifset,
  6419.          since that newline may also begin the next menu entry. */
  6420.           break;
  6421.         }
  6422.       else
  6423.         {
  6424.           if (action == IFSET)
  6425.         begin_insertion (ifset);
  6426.           else
  6427.         begin_insertion (ifclear);
  6428.         }
  6429.       }
  6430.       break;
  6431.     }
  6432.     }
  6433. }
  6434.  
  6435. /* **************************************************************** */
  6436. /*                                    */
  6437. /*            @itemx, @item                    */
  6438. /*                                    */
  6439. /* **************************************************************** */
  6440.  
  6441. /* Non-zero means a string is in execution, as opposed to a file. */
  6442. int executing_string = 0;
  6443.  
  6444. /* Execute the string produced by formatting the ARGs with FORMAT.  This
  6445.    is like submitting a new file with @include. */
  6446. #if __STDC__
  6447. void
  6448. execute_string (const char *format, ...)
  6449. {
  6450.   va_list args;
  6451.   static char temp_string[4000];
  6452.  
  6453.   va_start(args, format);
  6454.   vsprintf (temp_string, format, args);
  6455.   va_end(args);
  6456.  
  6457. #if 0
  6458. }  /* false balancing brace */
  6459. #endif
  6460. #else /* !(__STDC__) */
  6461. #if defined (HAVE_VARARGS_H) && defined(HAVE_VSPRINTF)
  6462.  
  6463. execute_string (va_alist)
  6464.      va_dcl
  6465. {
  6466.   static char temp_string[4000];
  6467.   char *format;
  6468.   va_list args;
  6469.  
  6470.   va_start (args);
  6471.   format = va_arg (args, char *);
  6472.   vsprintf (temp_string, format, args);
  6473.   va_end (args);
  6474.  
  6475. #if 0
  6476. }  /* false balancing brace */
  6477. #endif
  6478. #else /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
  6479.  
  6480. execute_string (format, arg1, arg2, arg3, arg4, arg5)
  6481.      char *format;
  6482. {
  6483.   static char temp_string[4000];
  6484.   sprintf (temp_string, format, arg1, arg2, arg3, arg4, arg5);
  6485.  
  6486. #endif /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */
  6487. #endif /* __STDC__ */
  6488.  
  6489.   strcat (temp_string, "@bye\n");
  6490.   pushfile ();
  6491.   input_text_offset = 0;
  6492.   input_text = temp_string;
  6493.   input_filename = savestring (input_filename);
  6494.   size_of_input_text = strlen (temp_string);
  6495.  
  6496.   executing_string++;
  6497.   reader_loop ();
  6498.  
  6499.   popfile ();
  6500.   executing_string--;
  6501.  
  6502.   free_and_clear (&command);
  6503.   command = savestring ("not bye");
  6504. }
  6505.  
  6506. int itemx_flag = 0;
  6507.  
  6508. void
  6509. cm_itemx ()
  6510. {
  6511.   itemx_flag++;
  6512.   cm_item ();
  6513.   itemx_flag--;
  6514. }
  6515.  
  6516. void
  6517. cm_item ()
  6518. {
  6519.   char *rest_of_line, *item_func;
  6520.  
  6521.   /* Can only hack "@item" while inside of an insertion. */
  6522.   if (insertion_level)
  6523.     {
  6524.       INSERTION_ELT *stack = insertion_stack;
  6525.       int original_input_text_offset;
  6526.  
  6527.       skip_whitespace ();
  6528.       original_input_text_offset = input_text_offset;
  6529.  
  6530.       get_rest_of_line (&rest_of_line);
  6531.       canon_white (rest_of_line);
  6532.       item_func = current_item_function ();
  6533.  
  6534.       /* Okay, do the right thing depending on which insertion function
  6535.      is active. */
  6536.  
  6537.     switch_top:
  6538.       switch (stack->insertion)
  6539.     {
  6540.     case ifinfo:
  6541.     case ifset:
  6542.     case ifclear:
  6543.     case cartouche:
  6544.       stack = stack->next;
  6545.       if (!stack)
  6546.         goto no_insertion;
  6547.       else
  6548.         goto switch_top;
  6549.       break;
  6550.  
  6551.     case menu:
  6552.     case quotation:
  6553.     case example:
  6554.     case smallexample:
  6555.     case lisp:
  6556.     case format:
  6557.     case display:
  6558.     case group:
  6559.       line_error ("The `@%s' command is meaningless within a `@%s' block",
  6560.               command,
  6561.               insertion_type_pname (current_insertion_type ()));
  6562.       break;
  6563.  
  6564.     case itemize:
  6565.     case enumerate:
  6566.       if (itemx_flag)
  6567.         {
  6568.           line_error ("@itemx is not meaningful inside of a `%s' block",
  6569.               insertion_type_pname (current_insertion_type ()));
  6570.         }
  6571.       else
  6572.         {
  6573.           start_paragraph ();
  6574.           kill_self_indent (-1);
  6575.           filling_enabled = indented_fill = 1;
  6576.  
  6577.           if (current_insertion_type () == itemize)
  6578.         {
  6579.           indent (output_column = current_indent - 2);
  6580.  
  6581.           /* I need some way to determine whether this command
  6582.              takes braces or not.  I believe the user can type
  6583.              either "@bullet" or "@bullet{}".  Of course, they
  6584.              can also type "o" or "#" or whatever else they want. */
  6585.           if (item_func && *item_func)
  6586.             {
  6587.               if (*item_func == '@')
  6588.             if (item_func[strlen (item_func) - 1] != '}')
  6589.               execute_string ("%s{}", item_func);
  6590.             else
  6591.               execute_string ("%s", item_func);
  6592.               else
  6593.             execute_string ("%s", item_func);
  6594.             }
  6595.           insert (' ');
  6596.           output_column++;
  6597.         }
  6598.           else
  6599.         enumerate_item ();
  6600.  
  6601.           /* Special hack.  This makes close paragraph ignore you until
  6602.          the start_paragraph () function has been called. */
  6603.           must_start_paragraph = 1;
  6604.  
  6605.           /* Ultra special hack.  It appears that some people incorrectly
  6606.          place text directly after the @item, instead of on a new line
  6607.          by itself.  This happens to work in TeX, so I make it work
  6608.          here. */
  6609.           if (*rest_of_line)
  6610.         {
  6611.           line_number--;
  6612.           input_text_offset = original_input_text_offset;
  6613.         }
  6614.         }
  6615.       break;
  6616.  
  6617.     case table:
  6618.     case ftable:
  6619.     case vtable:
  6620.       {
  6621.         /* Get rid of extra characters. */
  6622.         kill_self_indent (-1);
  6623.  
  6624.         /* close_paragraph () almost does what we want.  The problem
  6625.            is when paragraph_is_open, and last_char_was_newline, and
  6626.            the last newline has been turned into a space, because
  6627.            filling_enabled. I handle it here. */
  6628.         if (last_char_was_newline && filling_enabled && paragraph_is_open)
  6629.           insert ('\n');
  6630.         close_paragraph ();
  6631.  
  6632. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  6633.         /* Indent on a new line, but back up one indentation level. */
  6634.         {
  6635.           int t;
  6636.  
  6637.           t = inhibit_paragraph_indentation;
  6638.           inhibit_paragraph_indentation = 1;
  6639.           /* At this point, inserting any non-whitespace character will
  6640.          force the existing indentation to be output. */
  6641.           add_char ('i');
  6642.           inhibit_paragraph_indentation = t;
  6643.         }
  6644. #else /* !INDENT_PARAGRAPHS_IN_TABLE */
  6645.         add_char ('i');
  6646. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  6647.  
  6648.         output_paragraph_offset--;
  6649.         kill_self_indent (default_indentation_increment + 1);
  6650.  
  6651.         /* Add item's argument to the line. */
  6652.         filling_enabled = 0;
  6653.         if (item_func && *item_func)
  6654.            execute_string ("%s{%s}", item_func, rest_of_line);
  6655.          else
  6656.            execute_string ("%s", rest_of_line);
  6657.  
  6658.         if (current_insertion_type () == ftable)
  6659.           execute_string ("@findex %s\n", rest_of_line);
  6660.  
  6661.         if (current_insertion_type () == vtable)
  6662.           execute_string ("@vindex %s\n", rest_of_line);
  6663.  
  6664.         /* Start a new line, and let start_paragraph ()
  6665.            do the indenting of it for you. */
  6666.         close_single_paragraph ();
  6667.         indented_fill = filling_enabled = 1;
  6668.       }
  6669.     default:
  6670.       break;
  6671.     }
  6672.       free (rest_of_line);
  6673.     }
  6674.   else
  6675.     {
  6676.     no_insertion:
  6677.       line_error ("@%s found outside of an insertion block", command);
  6678.     }
  6679. }
  6680.  
  6681.  
  6682. /* **************************************************************** */
  6683. /*                                    */
  6684. /*            Defun and Friends                   */
  6685. /*                                    */
  6686. /* **************************************************************** */
  6687.  
  6688. #define DEFUN_SELF_DELIMITING(c)                    \
  6689.   (((c) == '(')                                \
  6690.    || ((c) == ')')                            \
  6691.    || ((c) == '[')                            \
  6692.    || ((c) == ']'))
  6693.  
  6694. /** already defined 
  6695. struct token_accumulator
  6696. {
  6697.   unsigned int length;
  6698.   unsigned int index;
  6699.   char **tokens;
  6700. };
  6701. **/
  6702.  
  6703. void
  6704. initialize_token_accumulator (accumulator)
  6705.      struct token_accumulator *accumulator;
  6706. {
  6707.   (accumulator->length) = 0;
  6708.   (accumulator->index) = 0;
  6709.   (accumulator->tokens) = NULL;
  6710. }
  6711.  
  6712. void
  6713. accumulate_token (accumulator, token)
  6714.      struct token_accumulator *accumulator;
  6715.      char *token;
  6716. {
  6717.   if ((accumulator->index) >= (accumulator->length))
  6718.     {
  6719.       (accumulator->length) += 10;
  6720.       (accumulator->tokens) = (char **) xrealloc
  6721.     (accumulator->tokens, (accumulator->length * sizeof (char *)));
  6722.     }
  6723.   accumulator->tokens[accumulator->index] = token;
  6724.   accumulator->index += 1;
  6725. }
  6726.  
  6727. char *
  6728. copy_substring (start, end)
  6729.      char *start;
  6730.      char *end;
  6731. {
  6732.   char *result, *scan, *scan_result;
  6733.  
  6734.   result = (char *) xmalloc ((end - start) + 1);
  6735.   scan_result = result;
  6736.   scan = start;
  6737.  
  6738.   while (scan < end)
  6739.     *scan_result++ = *scan++;
  6740.  
  6741.   *scan_result = '\0';
  6742.   return (result);
  6743. }
  6744.  
  6745. /* Given `string' pointing at an open brace, skip forward and return a
  6746.    pointer to just past the matching close brace. */
  6747. int
  6748. scan_group_in_string (string_pointer)
  6749.      char **string_pointer;
  6750. {
  6751.   register int c;
  6752.   register char *scan_string;
  6753.   register unsigned int level = 1;
  6754.  
  6755.   scan_string = (*string_pointer) + 1;
  6756.  
  6757.   while (1)
  6758.     {
  6759.       if (level == 0)
  6760.     {
  6761.       (*string_pointer) = scan_string;
  6762.       return (1);
  6763.     }
  6764.       c = (*scan_string++);
  6765.       if (c == '\0')
  6766.     {
  6767.       /* Tweak line_number to compensate for fact that
  6768.          we gobbled the whole line before coming here. */
  6769.       line_number -= 1;
  6770.       line_error ("Missing `}' in @def arg");
  6771.       line_number += 1;
  6772.       (*string_pointer) = (scan_string - 1);
  6773.       return (0);
  6774.     }
  6775.       if (c == '{')
  6776.     level += 1;
  6777.       if (c == '}')
  6778.     level -= 1;
  6779.     }
  6780. }
  6781.  
  6782. /* Return a list of tokens from the contents of `string'.
  6783.    Commands and brace-delimited groups count as single tokens.
  6784.    Contiguous whitespace characters are converted to a token
  6785.    consisting of a single space. */
  6786. char **
  6787. args_from_string (string)
  6788.      char *string;
  6789. {
  6790.   struct token_accumulator accumulator;
  6791.   register char *scan_string = string;
  6792.   char *token_start, *token_end;
  6793.  
  6794.   initialize_token_accumulator (&accumulator);
  6795.  
  6796.   while ((*scan_string) != '\0')
  6797.     {
  6798.       /* Replace arbitrary whitespace by a single space. */
  6799.       if (whitespace (*scan_string))
  6800.     {
  6801.       scan_string += 1;
  6802.       while (whitespace (*scan_string))
  6803.         scan_string += 1;
  6804.       accumulate_token ((&accumulator), (savestring (" ")));
  6805.       continue;
  6806.     }
  6807.  
  6808.       /* Commands count as single tokens. */
  6809.       if ((*scan_string) == COMMAND_PREFIX)
  6810.     {
  6811.       token_start = scan_string;
  6812.       scan_string += 1;
  6813.       if (self_delimiting (*scan_string))
  6814.         scan_string += 1;
  6815.       else
  6816.         {
  6817.           register int c;
  6818.           while (1)
  6819.         {
  6820.           c = *scan_string++;
  6821.  
  6822.            if ((c == '\0') || (c == '{') || (whitespace (c)))
  6823.             {
  6824.               scan_string -= 1;
  6825.               break;
  6826.             }
  6827.         }
  6828.  
  6829.           if (*scan_string == '{')
  6830.         {
  6831.           char *s = scan_string;
  6832.           (void) scan_group_in_string (&s);
  6833.           scan_string = s;
  6834.         }
  6835.         }
  6836.       token_end = scan_string;
  6837.     }
  6838.  
  6839.       /* Parentheses and brackets are self-delimiting. */
  6840.       else if (DEFUN_SELF_DELIMITING (*scan_string))
  6841.     {
  6842.       token_start = scan_string;
  6843.       scan_string += 1;
  6844.       token_end = scan_string;
  6845.     }
  6846.  
  6847.       /* Open brace introduces a group that is a single token. */
  6848.       else if (*scan_string == '{')
  6849.     {
  6850.       char *s = scan_string;
  6851.       int balanced = scan_group_in_string (&s);
  6852.  
  6853.       token_start = scan_string + 1;
  6854.       scan_string = s;
  6855.       token_end = balanced ? (scan_string - 1) : scan_string;
  6856.     }
  6857.  
  6858.       /* Otherwise a token is delimited by whitespace, parentheses,
  6859.      brackets, or braces.  A token is also ended by a command. */
  6860.       else
  6861.     {
  6862.       token_start = scan_string;
  6863.  
  6864.       while (1)
  6865.         {
  6866.           register int c;
  6867.  
  6868.           c = *scan_string++;
  6869.  
  6870.           if (!c ||
  6871.           (whitespace (c) || DEFUN_SELF_DELIMITING (c) ||
  6872.            c == '{' || c == '}'))
  6873.         {
  6874.           scan_string--;
  6875.           break;
  6876.         }
  6877.  
  6878.           /* If we encounter a command imbedded within a token,
  6879.          then end the token. */
  6880.           if (c == COMMAND_PREFIX)
  6881.         {
  6882.           scan_string--;
  6883.           break;
  6884.         }
  6885.         }
  6886.       token_end = scan_string;
  6887.     }
  6888.  
  6889.       accumulate_token
  6890.     (&accumulator, copy_substring (token_start, token_end));
  6891.     }
  6892.   accumulate_token (&accumulator, NULL);
  6893.   return (accumulator.tokens);
  6894. }
  6895.  
  6896. void
  6897. process_defun_args (defun_args, auto_var_p)
  6898.      char **defun_args;
  6899.      int auto_var_p;
  6900. {
  6901.   int pending_space = 0;
  6902.  
  6903.   while (1)
  6904.     {
  6905.       char *defun_arg = *defun_args++;
  6906.  
  6907.       if (defun_arg == NULL)
  6908.     break;
  6909.  
  6910.       if (defun_arg[0] == ' ')
  6911.     {
  6912.       pending_space = 1;
  6913.       continue;
  6914.     }
  6915.  
  6916.       if (pending_space)
  6917.     {
  6918.       add_char (' ');
  6919.       pending_space = 0;
  6920.     }
  6921.  
  6922.       if (DEFUN_SELF_DELIMITING (defun_arg[0]))
  6923.     add_char (defun_arg[0]);
  6924.       else if (defun_arg[0] == '&')
  6925.     add_word (defun_arg);
  6926.       else if (defun_arg[0] == COMMAND_PREFIX)
  6927.     execute_string ("%s", defun_arg);
  6928.       else if (auto_var_p)
  6929.     execute_string ("@var{%s}", defun_arg);
  6930.       else
  6931.     add_word (defun_arg);
  6932.     }
  6933. }
  6934.  
  6935. char *
  6936. next_nonwhite_defun_arg (arg_pointer)
  6937.      char ***arg_pointer;
  6938. {
  6939.   char **scan = (*arg_pointer);
  6940.   char *arg = (*scan++);
  6941.  
  6942.   if ((arg != 0) && (*arg == ' '))
  6943.     arg = *scan++;
  6944.  
  6945.   if (arg == 0)
  6946.     scan -= 1;
  6947.  
  6948.   *arg_pointer = scan;
  6949.  
  6950.   return ((arg == 0) ? "" : arg);
  6951. }
  6952.  
  6953. /* Make the defun type insertion.
  6954.    TYPE says which insertion this is.
  6955.    X_P says not to start a new insertion if non-zero. */
  6956. void
  6957. defun_internal (type, x_p)
  6958.      enum insertion_type type;
  6959.      int x_p;
  6960. {
  6961.   enum insertion_type base_type;
  6962.   char **defun_args, **scan_args;
  6963.   const char *category;
  6964.   char *defined_name, *type_name, *type_name2;
  6965.  
  6966.   {
  6967.     char *line;
  6968.     get_rest_of_line (&line);
  6969.     defun_args = (args_from_string (line));
  6970.     free (line);
  6971.   }
  6972.  
  6973.   scan_args = defun_args;
  6974.  
  6975.   switch (type)
  6976.     {
  6977.     case defun:
  6978.       category = "Function";
  6979.       base_type = deffn;
  6980.       break;
  6981.     case defmac:
  6982.       category = "Macro";
  6983.       base_type = deffn;
  6984.       break;
  6985.     case defspec:
  6986.       category = "Special Form";
  6987.       base_type = deffn;
  6988.       break;
  6989.     case defvar:
  6990.       category = "Variable";
  6991.       base_type = defvr;
  6992.       break;
  6993.     case defopt:
  6994.       category = "User Option";
  6995.       base_type = defvr;
  6996.       break;
  6997.     case deftypefun:
  6998.       category = "Function";
  6999.       base_type = deftypefn;
  7000.       break;
  7001.     case deftypevar:
  7002.       category = "Variable";
  7003.       base_type = deftypevr;
  7004.       break;
  7005.     case defivar:
  7006.       category = "Instance Variable";
  7007.       base_type = defcv;
  7008.       break;
  7009.     case defmethod:
  7010.       category = "Method";
  7011.       base_type = defop;
  7012.       break;
  7013.     case deftypemethod:
  7014.       category = "Method";
  7015.       base_type = deftypemethod;
  7016.       break;
  7017.     default:
  7018.       category = next_nonwhite_defun_arg (&scan_args);
  7019.       base_type = type;
  7020.       break;
  7021.     }
  7022.  
  7023.   if ((base_type == deftypefn)
  7024.       || (base_type == deftypevr)
  7025.       || (base_type == defcv)
  7026.       || (base_type == defop)
  7027.       || (base_type == deftypemethod))
  7028.     type_name = next_nonwhite_defun_arg (&scan_args);
  7029.  
  7030.   if (base_type == deftypemethod)
  7031.     type_name2 = next_nonwhite_defun_arg (&scan_args);
  7032.  
  7033.   defined_name = next_nonwhite_defun_arg (&scan_args);
  7034.  
  7035.   /* This hack exists solely for the purposes of formatting the texinfo
  7036.      manual.  I couldn't think of a better way.  The token might be
  7037.      a simple @@ followed immediately by more text.  If this is the case,
  7038.      then the next defun arg is part of this one, and we should concatenate
  7039.      them. */
  7040.   if (*scan_args && **scan_args && !whitespace (**scan_args) &&
  7041.       (strcmp (defined_name, "@@") == 0))
  7042.     {
  7043.       char *tem = (char *)xmalloc (3 + strlen (scan_args[0]));
  7044.  
  7045.       sprintf (tem, "@@%s", scan_args[0]);
  7046.  
  7047.       free (scan_args[0]);
  7048.       scan_args[0] = tem;
  7049.       scan_args++;
  7050.       defined_name = tem;
  7051.     }
  7052.  
  7053.   if (!x_p)
  7054.     begin_insertion (type);
  7055.  
  7056.   /* Write the definition header line.
  7057.      This should start at the normal indentation.  */
  7058.   current_indent -= default_indentation_increment;
  7059.   start_paragraph ();
  7060.  
  7061.   switch (base_type)
  7062.     {
  7063.     case deffn:
  7064.     case defvr:
  7065.     case deftp:
  7066.       execute_string (" -- %s: %s", category, defined_name);
  7067.       break;
  7068.     case deftypefn:
  7069.     case deftypevr:
  7070.       execute_string (" -- %s: %s %s", category, type_name, defined_name);
  7071.       break;
  7072.     case defcv:
  7073.       execute_string (" -- %s of %s: %s", category, type_name, defined_name);
  7074.       break;
  7075.     case defop:
  7076.       execute_string (" -- %s on %s: %s", category, type_name, defined_name);
  7077.       break;
  7078.     case deftypemethod:
  7079.       execute_string (" -- %s on %s: %s %s", category, type_name, type_name2,
  7080.               defined_name);
  7081.       break;
  7082.     default:
  7083.       break;
  7084.     }
  7085.   current_indent += default_indentation_increment;
  7086.  
  7087.   /* Now process the function arguments, if any.
  7088.      If these carry onto the next line, they should be indented by two
  7089.      increments to distinguish them from the body of the definition,
  7090.      which is indented by one increment.  */
  7091.   current_indent += default_indentation_increment;
  7092.  
  7093.   switch (base_type)
  7094.     {
  7095.     case deffn:
  7096.     case defop:
  7097.       process_defun_args (scan_args, 1);
  7098.       break;
  7099.     case deftp:
  7100.     case deftypefn:
  7101.     case deftypemethod:
  7102.       process_defun_args (scan_args, 0);
  7103.       break;
  7104.     default:
  7105.       break;
  7106.     }
  7107.   current_indent -= default_indentation_increment;
  7108.   close_single_paragraph ();
  7109.  
  7110.   /* Make an entry in the appropriate index. */
  7111.   switch (base_type)
  7112.     {
  7113.     case deffn:
  7114.     case deftypefn:
  7115.       execute_string ("@findex %s\n", defined_name);
  7116.       break;
  7117.     case defvr:
  7118.     case deftypevr:
  7119.     case defcv:
  7120.       execute_string ("@vindex %s\n", defined_name);
  7121.       break;
  7122.     case defop:
  7123.     case deftypemethod:
  7124.       execute_string ("@findex %s on %s\n", defined_name, type_name);
  7125.       break;
  7126.     case deftp:
  7127.       execute_string ("@tindex %s\n", defined_name);
  7128.       break;
  7129.     default:
  7130.       break;
  7131.     }
  7132.  
  7133.   /* Deallocate the token list. */
  7134.   scan_args = defun_args;
  7135.   while (1)
  7136.     {
  7137.       char * arg = (*scan_args++);
  7138.       if (arg == NULL)
  7139.     break;
  7140.       free (arg);
  7141.     }
  7142.   free (defun_args);
  7143. }
  7144.  
  7145. /* Add an entry for a function, macro, special form, variable, or option.
  7146.    If the name of the calling command ends in `x', then this is an extra
  7147.    entry included in the body of an insertion of the same type. */
  7148. void
  7149. cm_defun ()
  7150. {
  7151.   int x_p;
  7152.   enum insertion_type type;
  7153.   char *temp = savestring (command);
  7154.  
  7155.   x_p = (command[strlen (command) - 1] == 'x');
  7156.  
  7157.   if (x_p)
  7158.     temp[strlen (temp) - 1] = '\0';
  7159.  
  7160.   type = find_type_from_name (temp);
  7161.   free (temp);
  7162.  
  7163.   /* If we are adding to an already existing insertion, then make sure
  7164.      that we are already in an insertion of type TYPE. */
  7165.   if (x_p &&
  7166.       (!insertion_level || insertion_stack->insertion != type))
  7167.     {
  7168.       line_error ("Must be in a `%s' insertion in order to use `%s'x",
  7169.           command, command);
  7170.       discard_until ("\n");
  7171.       return;
  7172.     }
  7173.  
  7174.   defun_internal (type, x_p);
  7175. }
  7176.  
  7177. /* End existing insertion block. */
  7178. void
  7179. cm_end ()
  7180. {
  7181.   char *temp;
  7182.   enum insertion_type type;
  7183.  
  7184.   if (!insertion_level)
  7185.     {
  7186.       line_error ("Unmatched `@%s'", command);
  7187.       return;
  7188.     }
  7189.  
  7190.   get_rest_of_line (&temp);
  7191.   canon_white (temp);
  7192.  
  7193.   if (strlen (temp) == 0)
  7194.     line_error ("`@%s' needs something after it", command);
  7195.  
  7196.   type = find_type_from_name (temp);
  7197.  
  7198.   if (type == bad_type)
  7199.     {
  7200.       line_error ("Bad argument to `%s', `%s', using `%s'",
  7201.        command, temp, insertion_type_pname (current_insertion_type ()));
  7202.     }
  7203.   end_insertion (type);
  7204.   free (temp);
  7205. }
  7206.  
  7207.  
  7208. /* **************************************************************** */
  7209. /*                                    */
  7210. /*            Other Random Commands                   */
  7211. /*                                    */
  7212. /* **************************************************************** */
  7213.  
  7214. /* This says to inhibit the indentation of the next paragraph, but
  7215.    not of following paragraphs.  */
  7216. void
  7217. cm_noindent ()
  7218. {
  7219.   if (!inhibit_paragraph_indentation)
  7220.     inhibit_paragraph_indentation = -1;
  7221. }
  7222.  
  7223. /* I don't know exactly what to do with this.  Should I allow
  7224.    someone to switch filenames in the middle of output?  Since the
  7225.    file could be partially written, this doesn't seem to make sense.
  7226.    Another option: ignore it, since they don't *really* want to
  7227.    switch files.  Finally, complain, or at least warn. */
  7228. void
  7229. cm_setfilename ()
  7230. {
  7231.   char *filename;
  7232.   get_rest_of_line (&filename);
  7233.   /* warning ("`@%s %s' encountered and ignored", command, filename); */
  7234.   free (filename);
  7235. }
  7236.  
  7237. void
  7238. cm_ignore_line ()
  7239. {
  7240.   discard_until ("\n");
  7241. }
  7242.  
  7243. /* @br can be immediately followed by `{}', so we have to read those here.
  7244.    It should simply close the paragraph. */
  7245. void
  7246. cm_br ()
  7247. {
  7248.   if (looking_at ("{}"))
  7249.     input_text_offset += 2;
  7250.  
  7251.   if (curchar () == '\n')
  7252.     {
  7253.       input_text_offset++;
  7254.       line_number++;
  7255.     }
  7256.  
  7257.   close_paragraph ();
  7258. }
  7259.  
  7260.  /* Insert the number of blank lines passed as argument. */
  7261. void
  7262. cm_sp ()
  7263. {
  7264.   int lines;
  7265.   char *line;
  7266.  
  7267.   get_rest_of_line (&line);
  7268.  
  7269.   if (sscanf (line, "%d", &lines) != 1)
  7270.     {
  7271.       line_error ("%csp requires a positive numeric argument", COMMAND_PREFIX);
  7272.     }
  7273.   else
  7274.     {
  7275.       if (lines < 0)
  7276.     lines = 0;
  7277.  
  7278.       while (lines--)
  7279.     add_char ('\n');
  7280.     }
  7281.   free (line);
  7282. }
  7283.  
  7284. /* Start a new line with just this text on it.
  7285.    Then center the line of text.
  7286.    This always ends the current paragraph. */
  7287. void
  7288. cm_center ()
  7289. {
  7290.   register size_t i, start, length;
  7291.   int fudge_factor = 1;
  7292.   unsigned char *line;
  7293.  
  7294.   close_paragraph ();
  7295.   filling_enabled = indented_fill = 0;
  7296.   cm_noindent ();
  7297.   start = output_paragraph_offset;
  7298.   inhibit_output_flushing ();
  7299.   get_rest_of_line ((char **)&line);
  7300.   execute_string ((char *)line);
  7301.   free (line);
  7302.   uninhibit_output_flushing ();
  7303.  
  7304.   i = output_paragraph_offset - 1;
  7305.   while (i > (start - 1) && output_paragraph[i] == '\n')
  7306.     i--;
  7307.  
  7308.   output_paragraph_offset = ++i;
  7309.   length = output_paragraph_offset - start;
  7310.  
  7311.   if (length < (fill_column - fudge_factor))
  7312.     {
  7313.       line = (unsigned char *)xmalloc (1 + length);
  7314.       memcpy (line, (char *)(output_paragraph + start), length);
  7315.  
  7316.       i = (fill_column - fudge_factor - length) / 2;
  7317.       output_paragraph_offset = start;
  7318.  
  7319.       while (i--)
  7320.     insert (' ');
  7321.  
  7322.       for (i = 0; i < length; i++)
  7323.     insert (line[i]);
  7324.  
  7325.       free (line);
  7326.     }
  7327.  
  7328.   insert ('\n');
  7329.   close_paragraph ();
  7330.   filling_enabled = 1;
  7331. }
  7332.  
  7333. /* Show what an expression returns. */
  7334. void
  7335. cm_result (arg)
  7336.      int arg;
  7337. {
  7338.   if (arg == END)
  7339.     add_word ("=>");
  7340. }
  7341.  
  7342. /* What an expression expands to. */
  7343. void
  7344. cm_expansion (arg)
  7345.      int arg;
  7346. {
  7347.   if (arg == END)
  7348.     add_word ("==>");
  7349. }
  7350.  
  7351. /* Indicates two expressions are equivalent. */
  7352. void
  7353. cm_equiv (arg)
  7354.      int arg;
  7355. {
  7356.   if (arg == END)
  7357.     add_word ("==");
  7358. }
  7359.  
  7360. /* What an expression may print. */
  7361. void
  7362. cm_print (arg)
  7363.      int arg;
  7364. {
  7365.   if (arg == END)
  7366.     add_word ("-|");
  7367. }
  7368.  
  7369. /* An error signaled. */
  7370. void
  7371. cm_error (arg)
  7372.      int arg;
  7373. {
  7374.   if (arg == END)
  7375.     add_word ("error-->");
  7376. }
  7377.  
  7378. /* The location of point in an example of a buffer. */
  7379. void
  7380. cm_point (arg)
  7381.      int arg;
  7382. {
  7383.   if (arg == END)
  7384.     add_word ("-!-");
  7385. }
  7386.  
  7387. /* Start a new line with just this text on it.
  7388.    The text is outdented one level if possible. */
  7389. void
  7390. cm_exdent ()
  7391. {
  7392.   char *line;
  7393.   int i = current_indent;
  7394.  
  7395.   if (current_indent)
  7396.     current_indent -= default_indentation_increment;
  7397.  
  7398.   get_rest_of_line (&line);
  7399.   close_single_paragraph ();
  7400.   execute_string ("%s", line);
  7401.   current_indent = i;
  7402.   free (line);
  7403.   close_single_paragraph ();
  7404. }
  7405.  
  7406. void
  7407. cm_include ()
  7408. {
  7409.   cm_infoinclude ();
  7410. }
  7411.  
  7412. /* Remember this file, and move onto the next. */
  7413. void
  7414. cm_infoinclude ()
  7415. {
  7416.   char *filename;
  7417.  
  7418.   close_paragraph ();
  7419.   get_rest_of_line (&filename);
  7420.   pushfile ();
  7421.  
  7422.   /* In verbose mode we print info about including another file. */
  7423.   if (verbose_mode)
  7424.     {
  7425.       register int i = 0;
  7426.       register FSTACK *stack = filestack;
  7427.  
  7428.       for (i = 0, stack = filestack; stack; stack = stack->next, i++);
  7429.  
  7430.       i *= 2;
  7431.  
  7432.       printf ("%*s", i, "");
  7433.       printf ("%c%s %s\n", COMMAND_PREFIX, command, filename);
  7434.       fflush (stdout);
  7435.     }
  7436.  
  7437.   if (!find_and_load (filename))
  7438.     {
  7439.       extern char *sys_errlist[];
  7440.       extern int errno, sys_nerr;
  7441.       popfile ();
  7442.  
  7443.       /* Cannot "@include foo", in line 5 of "/wh/bar". */
  7444.       line_error ("`%c%s %s': %s", COMMAND_PREFIX, command, filename,
  7445.           ((errno < sys_nerr) ?
  7446.            sys_errlist[errno] : "Unknown file system error"));
  7447.     }
  7448.   free (filename);
  7449. }
  7450.  
  7451. /* The other side of a malformed expression. */
  7452. void
  7453. misplaced_brace ()
  7454. {
  7455.   line_error ("Misplaced `}'");
  7456. }
  7457.  
  7458. /* Don't let the filling algorithm insert extra whitespace here. */
  7459. void
  7460. cm_force_abbreviated_whitespace ()
  7461. {
  7462. }
  7463.  
  7464. /* Do not let this character signify the end of a sentence, though
  7465.    if it was seen without the command prefix it normally would.  We
  7466.    do this by turning on the 8th bit of the character. */
  7467. void
  7468. cm_ignore_sentence_ender ()
  7469. {
  7470.   add_char (META ((*command)));
  7471. }
  7472.  
  7473. /* Signals end of processing.  Easy to make this happen. */
  7474. void
  7475. cm_bye ()
  7476. {
  7477.   input_text_offset = size_of_input_text;
  7478. }
  7479.  
  7480. void
  7481. cm_asis ()
  7482. {
  7483. }
  7484.  
  7485. void
  7486. cm_math ()
  7487. {
  7488. }
  7489.  
  7490.  
  7491. /* **************************************************************** */
  7492. /*                                    */
  7493. /*            Indexing Stuff                    */
  7494. /*                                    */
  7495. /* **************************************************************** */
  7496.  
  7497.  
  7498. /* An index element... */
  7499. #if 0 /** already defined **/
  7500. typedef struct index_elt
  7501. {
  7502.   struct index_elt *next;
  7503.   char *entry;            /* The index entry itself. */
  7504.   char *node;            /* The node from whence it came. */
  7505.   int code;            /* Non-zero means add `@code{...}' when
  7506.                    printing this element. */
  7507.   size_t defining_line;        /* Line number where this entry was written. */
  7508. } INDEX_ELT;
  7509.  
  7510. /* A list of short-names for each index, and the index to that index in our
  7511.    index array, the_indices.  In addition, for each index, it is remembered
  7512.    whether that index is a code index or not.  Code indices have @code{}
  7513.    inserted around the first word when they are printed with printindex. */
  7514. typedef struct
  7515. {
  7516.   char *name;
  7517.   int index;
  7518.   int code;
  7519. } INDEX_ALIST;
  7520. #endif /** already defined **/
  7521.  
  7522. void
  7523. init_indices ()
  7524. {
  7525.   int i;
  7526.  
  7527.   /* Create the default data structures. */
  7528.  
  7529.   /* Initialize data space. */
  7530.   if (!the_indices)
  7531.     {
  7532.       the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) *
  7533.                         sizeof (INDEX_ELT *));
  7534.       the_indices[defined_indices] = (INDEX_ELT *) NULL;
  7535.  
  7536.       name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) *
  7537.                            sizeof (INDEX_ALIST *));
  7538.       name_index_alist[defined_indices] = (INDEX_ALIST *) NULL;
  7539.     }
  7540.  
  7541.   /* If there were existing indices, get rid of them now. */
  7542.   for (i = 0; i < defined_indices; i++)
  7543.     undefindex (name_index_alist[i]->name);
  7544.  
  7545.   /* Add the default indices. */
  7546.   defindex ("pg", 0);
  7547.   defindex ("fn", 1);        /* "fn" is a code index.  */
  7548.   defindex ("cp", 0);
  7549.   defindex ("vr", 0);
  7550.   defindex ("tp", 0);
  7551.   defindex ("ky", 0);
  7552.  
  7553. }
  7554.  
  7555. /* Find which element in the known list of indices has this name.
  7556.    Returns -1 if NAME isn't found. */
  7557. int
  7558. find_index_offset (name)
  7559.      const char *name;
  7560. {
  7561.   register int i;
  7562.   for (i = 0; i < defined_indices; i++)
  7563.     if (name_index_alist[i] &&
  7564.     strcmp (name, name_index_alist[i]->name) == 0)
  7565.       return (name_index_alist[i]->index);
  7566.   return (-1);
  7567. }
  7568.  
  7569. /* Return a pointer to the entry of (name . index) for this name.
  7570.    Return NULL if the index doesn't exist. */
  7571. INDEX_ALIST *
  7572. find_index (name)
  7573.      const char *name;
  7574. {
  7575.   int offset = find_index_offset (name);
  7576.   if (offset > -1)
  7577.     return (name_index_alist[offset]);
  7578.   else
  7579.     return ((INDEX_ALIST *) NULL);
  7580. }
  7581.  
  7582. /* Given an index name, return the offset in the_indices of this index,
  7583.    or -1 if there is no such index. */
  7584. int
  7585. translate_index (name)
  7586.      const char *name;
  7587. {
  7588.   INDEX_ALIST *which = find_index (name);
  7589.  
  7590.   if (which)
  7591.     return (which->index);
  7592.   else
  7593.     return (-1);
  7594. }
  7595.  
  7596. /* Return the index list which belongs to NAME. */
  7597. INDEX_ELT *
  7598. index_list (name)
  7599.      const char *name;
  7600. {
  7601.   int which = translate_index (name);
  7602.   if (which < 0)
  7603.     return ((INDEX_ELT *) -1L);
  7604.   else
  7605.     return (the_indices[which]);
  7606. }
  7607.  
  7608. /* Please release me, let me go... */
  7609. void
  7610. free_index (index)
  7611.      INDEX_ELT *index;
  7612. {
  7613.   INDEX_ELT *temp;
  7614.  
  7615.   while ((temp = index) != (INDEX_ELT *) NULL)
  7616.     {
  7617.       free (temp->entry);
  7618.       free (temp->node);
  7619.       index = index->next;
  7620.       free (temp);
  7621.     }
  7622. }
  7623.  
  7624. /* Flush an index by name. */
  7625. void
  7626. undefindex (name)
  7627.      const char *name;
  7628. {
  7629.   int i;
  7630.   int which = find_index_offset (name);
  7631.  
  7632.   if (which < 0)
  7633.     return; /*  (which); */
  7634.  
  7635.   i = name_index_alist[which]->index;
  7636.  
  7637.  
  7638.   free_index (the_indices[i]);
  7639.   the_indices[i] = (INDEX_ELT *) NULL;
  7640.  
  7641.   free (name_index_alist[which]->name);
  7642.   free (name_index_alist[which]);
  7643.   name_index_alist[which] = (INDEX_ALIST *) NULL;
  7644. }
  7645.  
  7646. /* Define an index known as NAME.  We assign the slot number.
  7647.    CODE if non-zero says to make this a code index. */
  7648. void
  7649. defindex (name, code)
  7650.      const char *name;
  7651.      int code;
  7652. {
  7653.   register int i, slot;
  7654.  
  7655.   /* If it already exists, flush it. */
  7656.   undefindex (name);
  7657.  
  7658.   /* Try to find an empty slot. */
  7659.   slot = -1;
  7660.   for (i = 0; i < defined_indices; i++)
  7661.     if (!name_index_alist[i])
  7662.       {
  7663.     slot = i;
  7664.     break;
  7665.       }
  7666.  
  7667.   if (slot < 0)
  7668.     {
  7669.       /* No such luck.  Make space for another index. */
  7670.       slot = defined_indices;
  7671.       defined_indices++;
  7672.  
  7673.       name_index_alist = (INDEX_ALIST **)
  7674.     xrealloc ((char *)name_index_alist,
  7675.           (1 + defined_indices) * sizeof (INDEX_ALIST *));
  7676.       the_indices = (INDEX_ELT **)
  7677.     xrealloc ((char *)the_indices,
  7678.           (1 + defined_indices) * sizeof (INDEX_ELT *));
  7679.     }
  7680.  
  7681.   /* We have a slot.  Start assigning. */
  7682.   name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST));
  7683.   name_index_alist[slot]->name = savestring (name);
  7684.   name_index_alist[slot]->index = slot;
  7685.   name_index_alist[slot]->code = code;
  7686.  
  7687.   the_indices[slot] = (INDEX_ELT *) NULL;
  7688. }
  7689.  
  7690. /* Add the arguments to the current index command to the index NAME. */
  7691. void
  7692. index_add_arg (name)
  7693.      const char *name;
  7694. {
  7695.   int which;
  7696.   char *index_entry;
  7697.   INDEX_ALIST *tem;
  7698.  
  7699.   tem = find_index (name);
  7700.  
  7701.   which = tem ? tem->index : -1;
  7702.  
  7703.   get_rest_of_line (&index_entry);
  7704.   ignore_blank_line ();
  7705.  
  7706.   if (which < 0)
  7707.     {
  7708.       line_error ("Unknown index reference `%s'", name);
  7709.       free (index_entry);
  7710.     }
  7711.   else
  7712.     {
  7713.       INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT));
  7714.       new->next = the_indices[which];
  7715.       new->entry = index_entry;
  7716.       new->node = current_node;
  7717.       new->code = tem->code;
  7718.       new->defining_line = line_number - 1;
  7719.       the_indices[which] = new;
  7720.     }
  7721. }
  7722.  
  7723. #define INDEX_COMMAND_SUFFIX "index"
  7724.  
  7725. /* The function which user defined index commands call. */
  7726. void
  7727. gen_index ()
  7728. {
  7729.   char *name = savestring (command);
  7730.   if (strlen (name) >= strlen ("index"))
  7731.     name[strlen (name) - strlen ("index")] = '\0';
  7732.   index_add_arg (name);
  7733.   free (name);
  7734. }
  7735.  
  7736. /* Define a new index command.  Arg is name of index. */
  7737. void
  7738. cm_defindex ()
  7739. {
  7740.   gen_defindex (0);
  7741. }
  7742.  
  7743. void
  7744. cm_defcodeindex ()
  7745. {
  7746.   gen_defindex (1);
  7747. }
  7748.  
  7749. void
  7750. gen_defindex (code)
  7751.      int code;
  7752. {
  7753.   char *name;
  7754.   get_rest_of_line (&name);
  7755.  
  7756.   if (find_index (name))
  7757.     {
  7758.       line_error ("Index `%s' already exists", name);
  7759.       free (name);
  7760.       return;
  7761.     }
  7762.   else
  7763.     {
  7764.       char *temp = (char *) alloca (1 + strlen (name) + strlen ("index"));
  7765.       sprintf (temp, "%sindex", name);
  7766.       define_user_command (temp, (FUNCTION *)gen_index, 0);
  7767.       defindex (name, code);
  7768.       free (name);
  7769.     }
  7770. }
  7771.  
  7772. /* Append LIST2 to LIST1.  Return the head of the list. */
  7773. INDEX_ELT *
  7774. index_append (head, tail)
  7775.      INDEX_ELT *head, *tail;
  7776. {
  7777.   register INDEX_ELT *t_head = head;
  7778.  
  7779.   if (!t_head)
  7780.     return (tail);
  7781.  
  7782.   while (t_head->next)
  7783.     t_head = t_head->next;
  7784.   t_head->next = tail;
  7785.   return (head);
  7786. }
  7787.  
  7788. /* Expects 2 args, on the same line.  Both are index abbreviations.
  7789.    Make the first one be a synonym for the second one, i.e. make the
  7790.    first one have the same index as the second one. */
  7791. void
  7792. cm_synindex ()
  7793. {
  7794.   int redirector, redirectee;
  7795.   char *temp;
  7796.  
  7797.   skip_whitespace ();
  7798.   temp = read_token(); /* was --  get_until_in_line (" ", &temp); */
  7799.   redirectee = find_index_offset (temp);
  7800.   skip_whitespace ();
  7801.   free_and_clear (&temp);
  7802.   temp = read_token(); /* was --  get_until_in_line (" ", &temp); */
  7803.   redirector = find_index_offset (temp);
  7804.   free (temp);
  7805.   if (redirector < 0 || redirectee < 0)
  7806.     {
  7807.       line_error ("Unknown index reference");
  7808.     }
  7809.   else
  7810.     {
  7811.       /* I think that we should let the user make indices synonymous to
  7812.          each other without any lossage of info.  This means that one can
  7813.          say @synindex cp dt anywhere in the file, and things that used to
  7814.          be in cp will go into dt. */
  7815.       INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector];
  7816.  
  7817.       if (i1 || i2)
  7818.     {
  7819.       if (i1)
  7820.         the_indices[redirectee] = index_append (i1, i2);
  7821.       else
  7822.         the_indices[redirectee] = index_append (i2, i1);
  7823.     }
  7824.  
  7825.       name_index_alist[redirectee]->index =
  7826.     name_index_alist[redirector]->index;
  7827.     }
  7828. }
  7829.  
  7830. void
  7831. cm_pindex ()            /* Pinhead index. */
  7832. {
  7833.   index_add_arg ("pg");
  7834. }
  7835.  
  7836. void
  7837. cm_vindex ()            /* Variable index. */
  7838. {
  7839.   index_add_arg ("vr");
  7840. }
  7841.  
  7842. void
  7843. cm_kindex ()            /* Key index. */
  7844. {
  7845.   index_add_arg ("ky");
  7846. }
  7847.  
  7848. void
  7849. cm_cindex ()            /* Concept index. */
  7850. {
  7851.   index_add_arg ("cp");
  7852. }
  7853.  
  7854. void
  7855. cm_findex ()            /* Function index. */
  7856. {
  7857.   index_add_arg ("fn");
  7858. }
  7859.  
  7860. void
  7861. cm_tindex ()            /* Data Type index. */
  7862. {
  7863.   index_add_arg ("tp");
  7864. }
  7865.  
  7866. /* Sorting the index. */
  7867. int
  7868. index_element_compare (element1, element2)
  7869.      INDEX_ELT **element1, **element2;
  7870. {
  7871.   /* This needs to ignore leading non-text characters. */
  7872.   return (stricmp ((*element1)->entry, (*element2)->entry));
  7873. }
  7874.  
  7875. /* Sort the index passed in INDEX, returning an array of
  7876.    pointers to elements.  The array is terminated with a NULL
  7877.    pointer.  We call qsort because it's supposed to be fast.
  7878.    I think this looks bad. */
  7879. INDEX_ELT **
  7880. sort_index (index)
  7881.      INDEX_ELT *index;
  7882. {
  7883.   INDEX_ELT *temp = index;
  7884.   INDEX_ELT **array;
  7885.   int count = 0;
  7886.  
  7887.   while (temp != (INDEX_ELT *) NULL)
  7888.     {
  7889.       count++;
  7890.       temp = temp->next;
  7891.     }
  7892.  
  7893.   /* We have the length.  Make an array. */
  7894.  
  7895.   array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *));
  7896.   count = 0;
  7897.   temp = index;
  7898.  
  7899.   while (temp != (INDEX_ELT *) NULL)
  7900.     {
  7901.       array[count++] = temp;
  7902.       temp = temp->next;
  7903.     }
  7904.   array[count] = (INDEX_ELT *) NULL;    /* terminate the array. */
  7905.  
  7906.   /* Sort the array. */
  7907.   qsort (array, (size_t)count, sizeof (INDEX_ELT *), index_element_compare);
  7908.  
  7909.   return (array);
  7910. }
  7911.  
  7912. /* Non-zero means that we are in the middle of printing an index. */
  7913. int printing_index = 0;
  7914.  
  7915. /* Takes one arg, a short name of an index to print.
  7916.    Outputs a menu of the sorted elements of the index. */
  7917. void
  7918. cm_printindex ()
  7919. {
  7920.   int item;
  7921.   INDEX_ELT *index;
  7922.   INDEX_ELT **array;
  7923.   char *index_name;
  7924.   int old_inhibitions = inhibit_paragraph_indentation;
  7925.   int previous_filling_enabled_value = filling_enabled;
  7926.  
  7927.   close_paragraph ();
  7928.   get_rest_of_line (&index_name);
  7929.  
  7930.   index = index_list (index_name);
  7931.   if ((long) index == -1L)
  7932.     {
  7933.       line_error ("Unknown index name `%s'", index_name);
  7934.       free (index_name);
  7935.       return;
  7936.     }
  7937.   else
  7938.     free (index_name);
  7939.  
  7940.   array = sort_index (index);
  7941.  
  7942.   filling_enabled = 0;
  7943.   inhibit_paragraph_indentation = 1;
  7944.   close_paragraph ();
  7945.   add_word ("* Menu:\n\n");
  7946.  
  7947.   printing_index = 1;
  7948.   for (item = 0; (index = array[item]); item++)
  7949.     {
  7950.       size_t real_line_number = line_number;
  7951.  
  7952.       /* Let errors generated while making the index entry point back
  7953.      at the line which contains the entry. */
  7954.       line_number = index->defining_line;
  7955.  
  7956.       /* If this particular entry should be printed as a "code" index,
  7957.      then wrap the entry with "@code{...}". */
  7958.       if (index->code)
  7959.     execute_string ("* @code{%s}: ", index->entry);
  7960.       else
  7961.     execute_string ("* %s: ", index->entry);
  7962.  
  7963.       /* Pad the front of the destination nodename so that
  7964.      the output looks nice. */
  7965.       if (fill_column > 40 && output_column < 40)
  7966.     indent (40 - output_column);
  7967.  
  7968.       execute_string ("%s.\n", index->node);
  7969.  
  7970.       line_number = real_line_number;
  7971.       flush_output ();
  7972.     }
  7973.  
  7974.   printing_index = 0;
  7975.   free (array);
  7976.   close_single_paragraph ();
  7977.   filling_enabled = previous_filling_enabled_value;
  7978.   inhibit_paragraph_indentation = old_inhibitions;
  7979. }
  7980.  
  7981.  
  7982. /* **************************************************************** */
  7983. /*                                    */
  7984. /*            Making User Defined Commands            */
  7985. /*                                    */
  7986. /* **************************************************************** */
  7987.  
  7988. void
  7989. define_user_command (name, proc, needs_braces_p)
  7990.      char *name;
  7991.      FUNCTION *proc;
  7992.      int needs_braces_p;
  7993. {
  7994.   int slot = user_command_array_len;
  7995.   user_command_array_len++;
  7996.  
  7997.   if (!user_command_array)
  7998.     user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *));
  7999.  
  8000.   user_command_array = (COMMAND **) xrealloc (user_command_array,
  8001.                           (1 + user_command_array_len) *
  8002.                           sizeof (COMMAND *));
  8003.  
  8004.   user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND));
  8005.   user_command_array[slot]->name = savestring (name);
  8006.   user_command_array[slot]->proc = proc;
  8007.   user_command_array[slot]->argument_in_braces = needs_braces_p;
  8008. }
  8009.  
  8010. /* Make ALIAS run the named FUNCTION.  Copies properties from FUNCTION. */
  8011. void
  8012. define_alias (alias, function)
  8013.      char *alias, *function;
  8014. {
  8015. }
  8016.  
  8017. /* Set the paragraph indentation variable to the value specified in STRING.
  8018.    Values can be:
  8019.    `asis': Don't change existing indentation.
  8020.    `none': Remove existing indentation.
  8021.       NUM: Indent NUM spaces at the starts of paragraphs.
  8022.            Note that if NUM is zero, we assume `none'.
  8023.  
  8024.    Returns 0 if successful, or non-zero if STRING isn't one of the above. */
  8025. int
  8026. set_paragraph_indent (string)
  8027.      char *string;
  8028. {
  8029.   if (strcmp (string, "asis") == 0)
  8030.     paragraph_start_indent = 0;
  8031.   else if (strcmp (string, "none") == 0)
  8032.     paragraph_start_indent = -1;
  8033.   else
  8034.     {
  8035.       if (sscanf (string, "%d", ¶graph_start_indent) != 1)
  8036.     return (-1);
  8037.       else
  8038.     {
  8039.       if (paragraph_start_indent == 0)
  8040.         paragraph_start_indent = -1;
  8041.     }
  8042.     }
  8043.   return (0);
  8044. }
  8045.  
  8046. void
  8047. cm_paragraphindent ()
  8048. {
  8049.   char *arg;
  8050.  
  8051.   get_rest_of_line (&arg);
  8052.   if (set_paragraph_indent (arg) != 0)
  8053.     line_error ("Bad argument to @paragraphindent");
  8054.  
  8055.   free (arg);
  8056. }
  8057.  
  8058. /* Some support for footnotes. */
  8059.  
  8060. /* Footnotes are a new construct in Info.  We don't know the best method
  8061.    of implementing them for sure, so we present two possiblities.
  8062.  
  8063.    SeparateNode:
  8064.     Make them look like followed references, with the reference
  8065.     destinations in a makeinfo manufactured node or,
  8066.  
  8067.    EndNode:
  8068.     Make them appear at the bottom of the node that they originally
  8069.     appeared in. */
  8070. #define SeparateNode 0
  8071. #define EndNode 1
  8072.  
  8073. int footnote_style = EndNode;
  8074. int first_footnote_this_node = 1;
  8075. int footnote_count = 0;
  8076.  
  8077. /* Set the footnote style based on he style identifier in STRING. */
  8078. int
  8079. set_footnote_style (string)
  8080.      char *string;
  8081. {
  8082.   if ((stricmp (string, "separate") == 0) ||
  8083.       (stricmp (string, "MN") == 0))
  8084.     footnote_style = SeparateNode;
  8085.   else if ((stricmp (string, "end") == 0) ||
  8086.        (stricmp (string, "EN") == 0))
  8087.     footnote_style = EndNode;
  8088.   else
  8089.     return (-1);
  8090.  
  8091.  return (0);
  8092. }
  8093.  
  8094. void
  8095. cm_footnotestyle ()
  8096. {
  8097.   char *arg;
  8098.  
  8099.   get_rest_of_line (&arg);
  8100.  
  8101.   if (set_footnote_style (arg) != 0)
  8102.     line_error ("Bad argument to @footnotestyle");
  8103.  
  8104.   free (arg);
  8105. }
  8106.  
  8107. /** already defined
  8108. typedef struct fn
  8109. {
  8110.   struct fn *next;
  8111.   char *marker;
  8112.   char *note;
  8113. }  FN;
  8114. **/
  8115.  
  8116. FN *pending_notes = (FN *) NULL;
  8117.  
  8118. /* A method for remembering footnotes.  Note that this list gets output
  8119.    at the end of the current node. */
  8120. void
  8121. remember_note (marker, note)
  8122.      char *marker, *note;
  8123. {
  8124.   FN *temp = (FN *) xmalloc (sizeof (FN));
  8125.  
  8126.   temp->marker = savestring (marker);
  8127.   temp->note = savestring (note);
  8128.   temp->next = pending_notes;
  8129.   pending_notes = temp;
  8130.   footnote_count++;
  8131. }
  8132.  
  8133. /* How to get rid of existing footnotes. */
  8134. void
  8135. free_pending_notes ()
  8136. {
  8137.   FN *temp;
  8138.  
  8139.   while ((temp = pending_notes) != (FN *) NULL)
  8140.     {
  8141.       free (temp->marker);
  8142.       free (temp->note);
  8143.       pending_notes = pending_notes->next;
  8144.       free (temp);
  8145.     }
  8146.   first_footnote_this_node = 1;
  8147.   footnote_count = 0;
  8148. }
  8149.  
  8150. /* What to do when you see a @footnote construct. */
  8151.  
  8152.  /* Handle a "footnote".
  8153.     footnote *{this is a footnote}
  8154.     where "*" is the marker character for this note. */
  8155. void
  8156. cm_footnote ()
  8157. {
  8158.   char *marker;
  8159.   char *note;
  8160.  
  8161.   get_until ("{", &marker);
  8162.   canon_white (marker);
  8163.  
  8164.   /* Read the argument in braces. */
  8165.   if (curchar () != '{')
  8166.     {
  8167.       line_error ("`@%s' expected more than just `%s'.  It needs something in `{...}'", command, marker);
  8168.       free (marker);
  8169.       return;
  8170.     }
  8171.   else
  8172.     {
  8173.       int braces = 1;
  8174.       size_t temp = ++input_text_offset;
  8175.       size_t len;
  8176.  
  8177.       while (braces)
  8178.     {
  8179.       if (temp == size_of_input_text)
  8180.         {
  8181.           line_error ("No closing brace for footnote `%s'", marker);
  8182.           return;
  8183.         }
  8184.  
  8185.       if (input_text[temp] == '{')
  8186.         braces++;
  8187.       else if (input_text[temp] == '}')
  8188.         braces--;
  8189.       else if (input_text[temp] == '\n')
  8190.         line_number ++;
  8191.  
  8192.       temp++;
  8193.     }
  8194.  
  8195.       len = (temp - input_text_offset) - 1;
  8196.       note = (char *)xmalloc (len + 1);
  8197.       strncpy (note, &input_text[input_text_offset], len);
  8198.       note[len] = '\0';
  8199.       input_text_offset = temp;
  8200.     }
  8201.  
  8202.   if (!current_node || !*current_node)
  8203.     {
  8204.       line_error ("Footnote defined without parent node");
  8205.       free (marker);
  8206.       free (note);
  8207.       return;
  8208.     }
  8209.  
  8210.   if (!*marker)
  8211.     {
  8212.       free (marker);
  8213.  
  8214.       if (number_footnotes)
  8215.     {
  8216.       marker = (char *)xmalloc ((size_t)10);
  8217.       sprintf (marker, "%d", current_footnote_number);
  8218.       current_footnote_number++;
  8219.     }
  8220.       else
  8221.     marker = savestring ("*");
  8222.     }
  8223.  
  8224.   remember_note (marker, note);
  8225.  
  8226.   /* Your method should at least insert MARKER. */
  8227.   switch (footnote_style)
  8228.     {
  8229.     case SeparateNode:
  8230.       add_word_args ("(%s)", marker);
  8231.       if (first_footnote_this_node)
  8232.     {
  8233.       char *temp_string;
  8234.  
  8235.       temp_string = (char *)
  8236.         xmalloc ((strlen (current_node)) + (strlen ("-Footnotes")) + 1);
  8237.  
  8238.       add_word_args (" (*note %s-Footnotes::)", current_node);
  8239.       strcpy (temp_string, current_node);
  8240.       strcat (temp_string, "-Footnotes");
  8241.       remember_node_reference (temp_string, line_number, followed_reference);
  8242.       free (temp_string);
  8243.       first_footnote_this_node = 0;
  8244.     }
  8245.       break;
  8246.  
  8247.     case EndNode:
  8248.       add_word_args ("(%s)", marker);
  8249.       break;
  8250.  
  8251.     default:
  8252.       break;
  8253.     }
  8254.   free (marker);
  8255.   free (note);
  8256. }
  8257.  
  8258. /* Non-zero means that we are currently in the process of outputting
  8259.    footnotes. */
  8260. int already_outputting_pending_notes = 0;
  8261.  
  8262. /* Output the footnotes.  We are at the end of the current node. */
  8263. void
  8264. output_pending_notes ()
  8265. {
  8266.   FN *footnote = pending_notes;
  8267.  
  8268.   if (!pending_notes)
  8269.     return;
  8270.  
  8271.   switch (footnote_style)
  8272.     {
  8273.  
  8274.     case SeparateNode:
  8275.       {
  8276.     char *old_current_node = current_node;
  8277.     char *old_command = savestring (command);
  8278.  
  8279.     already_outputting_pending_notes++;
  8280.     execute_string ("@node %s-Footnotes,,,%s\n", current_node, current_node);
  8281.     already_outputting_pending_notes--;
  8282.     current_node = old_current_node;
  8283.     free (command);
  8284.     command = old_command;
  8285.       }
  8286.       break;
  8287.  
  8288.     case EndNode:
  8289.       close_paragraph ();
  8290.       in_fixed_width_font++;
  8291.       execute_string ("---------- Footnotes ----------\n\n");
  8292.       in_fixed_width_font--;
  8293.       break;
  8294.     }
  8295.  
  8296.   /* Handle the footnotes in reverse order. */
  8297.   {
  8298.     FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *));
  8299.  
  8300.     array[footnote_count] = (FN *) NULL;
  8301.  
  8302.     while (--footnote_count > -1)
  8303.       {
  8304.     array[footnote_count] = footnote;
  8305.     footnote = footnote->next;
  8306.       }
  8307.  
  8308.     filling_enabled = 1;
  8309.     indented_fill = 1;
  8310.  
  8311.     while ((footnote = array[++footnote_count]))
  8312.       {
  8313.  
  8314.     switch (footnote_style)
  8315.       {
  8316.       case SeparateNode:
  8317.       case EndNode:
  8318.         execute_string ("(%s)  %s", footnote->marker, footnote->note);
  8319.         close_paragraph ();
  8320.         break;
  8321.       }
  8322.       }
  8323.     close_paragraph ();
  8324.     free (array);
  8325.   }
  8326. }
  8327.  
  8328.  
  8329. /* **************************************************************** */
  8330. /*                                                                  */
  8331. /*              User definable Macros (text substitution)        */
  8332. /*                                                                  */
  8333. /* **************************************************************** */
  8334.  
  8335. #if defined (HAVE_MACROS)
  8336.  
  8337. /* Array of macros and definitions. */
  8338. MACRO_DEF **macro_list = (MACRO_DEF **)NULL;
  8339.  
  8340. int macro_list_len = 0;        /* Number of elements. */
  8341. int macro_list_size = 0;    /* Number of slots in total. */
  8342.  
  8343. /* Return the macro definition of NAME or NULL if NAME is not defined. */
  8344. MACRO_DEF *
  8345. find_macro (name)
  8346.      char *name;
  8347. {
  8348.   register int i;
  8349.   register MACRO_DEF *def;
  8350.  
  8351.   def = (MACRO_DEF *)NULL;
  8352.   for (i = 0; macro_list && (def = macro_list[i]); i++)
  8353.     if (strcmp (def->name, name) == 0)
  8354.       break;
  8355.  
  8356.   return (def);
  8357. }
  8358.  
  8359. /* Add the macro NAME with DEFINITION to macro_list.  FILENAME is
  8360.    the name of the file where this definition can be found, and
  8361.    LINENO is the line number within that file.  If a macro already
  8362.    exists with NAME, then a warning is produced, and that previous
  8363.    definition is overwritten. */
  8364. void
  8365. add_macro (name, definition, filename, lineno)
  8366.      char *name, *definition;
  8367.      char *filename;
  8368.      size_t lineno;
  8369. {
  8370.   register MACRO_DEF *def;
  8371.  
  8372.   def = find_macro (name);
  8373.  
  8374.   if (!def)
  8375.     {
  8376.       if (macro_list_len + 2 >= macro_list_size)
  8377.     macro_list = (MACRO_DEF **)xrealloc
  8378.       (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
  8379.  
  8380.       macro_list[macro_list_len] = (MACRO_DEF *)xmalloc (sizeof (MACRO_DEF));
  8381.       macro_list[macro_list_len + 1] = (MACRO_DEF *)NULL;
  8382.  
  8383.       def = macro_list[macro_list_len];
  8384.       macro_list_len += 1;
  8385.       def->name = savestring (name);
  8386.     }
  8387.   else
  8388.     {
  8389.       char *temp_filename = input_filename;
  8390.       size_t temp_line = line_number;
  8391.  
  8392.       warning ("The macro `%s' is previously defined.", name);
  8393.  
  8394.       input_filename = def->filename;
  8395.       line_number = def->lineno;
  8396.  
  8397.       warning ("Here is the previous definition of `%s'.", name);
  8398.  
  8399.       input_filename = temp_filename;
  8400.       line_number = temp_line;
  8401.  
  8402.       free (def->filename);
  8403.       free (def->definition);
  8404.     }
  8405.  
  8406.   def->filename = savestring (filename);
  8407.   def->lineno = lineno;
  8408.   def->definition = savestring (definition);
  8409. }
  8410.  
  8411.  
  8412. /* Delete the macro with name NAME.  The macro is deleted from the list,
  8413.    but it is also returned.  If there was no macro defined, NULL is
  8414.    returned. */
  8415. MACRO_DEF *
  8416. delete_macro (name)
  8417.      char *name;
  8418. {
  8419.   register int i;
  8420.   register MACRO_DEF *def;
  8421.  
  8422.   def = (MACRO_DEF *)NULL;
  8423.   for (i = 0; macro_list && (def = macro_list[i]); i++)
  8424.     if (strcmp (def->name, name) == 0)
  8425.       {
  8426.     memcpy (macro_list + i, macro_list + i + 1,
  8427.            ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
  8428.     break;
  8429.       }
  8430.   return (def);
  8431. }
  8432.  
  8433. /* Execute the macro passed in DEF, a pointer to a MACRO_DEF. */
  8434. void
  8435. execute_macro (def)
  8436.      MACRO_DEF *def;
  8437. {
  8438.  
  8439.   if (def != (MACRO_DEF *)NULL)
  8440.     {
  8441.       char *line, *string;
  8442.  
  8443.       get_until ("\n", &line);
  8444.  
  8445.       if (curchar () == '\n')    /* as opposed to the end of the file... */
  8446.     {
  8447.       line_number++;
  8448.       input_text_offset++;
  8449.     }
  8450.       string = (char *)xmalloc (1 + strlen (def->definition) + strlen (line));
  8451.       strcpy (string, def->definition);
  8452.       strcat (string, line);
  8453.       free (line);
  8454.  
  8455.       execute_string ("%s\n", string);
  8456.       free (string);
  8457.     }
  8458. }
  8459.  
  8460. void
  8461. cm_macro ()
  8462. {
  8463.   register size_t i;
  8464.   char *line, *name/* , *expansion */;
  8465.  
  8466.   get_rest_of_line (&line);
  8467.   canon_white (line);
  8468.  
  8469.   for (i = 0; line[i] && !whitespace (line[i]); i++);
  8470.   name = (char *)xmalloc (i);
  8471.   strncpy (name, line, i);
  8472.   name[i] = '\0';
  8473.  
  8474.   while (whitespace (line[i]))
  8475.     i++;
  8476.  
  8477.   add_macro (name, line + i, input_filename, line_number - 1);
  8478.   free (line);
  8479.   free (name);
  8480. }
  8481.  
  8482. void
  8483. cm_unmacro ()
  8484. {
  8485.   register size_t i;
  8486.   char *line, *name;
  8487.   MACRO_DEF *def;
  8488.  
  8489.   get_rest_of_line (&line);
  8490.   canon_white (line);
  8491.  
  8492.   for (i = 0; line[i] && !whitespace (line[i]); i++);
  8493.   name = (char *)xmalloc (i);
  8494.   strncpy (name, line, i);
  8495.   name[i] = '\0';
  8496.  
  8497.   def = delete_macro (name);
  8498.  
  8499.   if (def)
  8500.     {
  8501.       free (def->filename);
  8502.       free (def->name);
  8503.       free (def->definition);
  8504.       free (def);
  8505.     }
  8506.  
  8507.   free (line);
  8508.   free (name);
  8509. }
  8510. #endif /* HAVE_MACROS */
  8511.  
  8512. /* **************************************************************** */
  8513. /*                                                                  */
  8514. /*                  Looking For Include Files                       */
  8515. /*                                                                  */
  8516. /* **************************************************************** */
  8517.  
  8518. #ifndef atarist  /* we need different search strategy, since ':'
  8519.           * can be a valid path character */
  8520. /* Given a string containing units of information separated by colons,
  8521.    return the next one pointed to by INDEX, or NULL if there are no more.
  8522.    Advance INDEX to the character after the colon. */
  8523. char *
  8524. extract_colon_unit (string, index)
  8525.      char *string;
  8526.      int *index;
  8527. {
  8528.   size_t i, start;
  8529.  
  8530.   i = *index;
  8531.  
  8532.   if (!string || (i >= strlen (string)))
  8533.     return ((char *)NULL);
  8534.  
  8535.   /* Each call to this routine leaves the index pointing at a colon if
  8536.      there is more to the path.  If I is > 0, then increment past the
  8537.      `:'.  If I is 0, then the path has a leading colon.  Trailing colons
  8538.      are handled OK by the `else' part of the if statement; an empty
  8539.      string is returned in that case. */
  8540.   if (i && string[i] == ':')
  8541.     i++;
  8542.  
  8543.   start = i;
  8544.  
  8545.   while (string[i] && string[i] != ':') i++;
  8546.  
  8547.   *index = i;
  8548.  
  8549.   if (i == start)
  8550.     {
  8551.       if (string[i])
  8552.     (*index)++;
  8553.  
  8554.       /* Return "" in the case of a trailing `:'. */
  8555.       return (savestring (""));
  8556.     }
  8557.   else
  8558.     {
  8559.       char *value;
  8560.  
  8561.       value = (char *)xmalloc (1 + (i - start));
  8562.       strncpy (value, &string[start], (i - start));
  8563.       value [i - start] = '\0';
  8564.  
  8565.       return (value);
  8566.     }
  8567. }
  8568.  
  8569. /* Return the full pathname for FILENAME by searching along PATH.
  8570.    When found, return the stat () info for FILENAME in FINFO.
  8571.    If PATH is NULL, only the current directory is searched.
  8572.    If the file could not be found, return a NULL pointer. */
  8573. char *
  8574. get_file_info_in_path (filename, path, finfo)
  8575.      char *filename, *path;
  8576.      struct stat *finfo;
  8577. {
  8578.   char *dir;
  8579.   int result, index = 0;
  8580.  
  8581.   if (path == (char *)NULL)
  8582.     path = ".";
  8583.  
  8584.   /* Handle absolute pathnames. "./foo", "/foo", "../foo". */
  8585.   if (*filename == '/' ||
  8586.       (*filename == '.' &&
  8587.        (filename[1] == '/' ||
  8588.     (filename[1] == '.' && filename[2] == '/'))))
  8589.     {
  8590.       if (stat (filename, finfo) == 0)
  8591.     return (savestring (filename));
  8592.       else
  8593.     return ((char *)NULL);
  8594.     }
  8595.  
  8596.   while ((dir = extract_colon_unit (path, &index)))
  8597.     {
  8598.       char *fullpath;
  8599.  
  8600.       if (!*dir)
  8601.     {
  8602.       free (dir);
  8603.       dir = savestring (".");
  8604.     }
  8605.  
  8606.       fullpath = (char *)xmalloc (2 + strlen (dir) + strlen (filename));
  8607.       sprintf (fullpath, "%s/%s", dir, filename);
  8608.       free (dir);
  8609.  
  8610.       result = stat (fullpath, finfo);
  8611.  
  8612.       if (result == 0)
  8613.     return (fullpath);
  8614.       else
  8615.     free (fullpath);
  8616.     }
  8617.   return ((char *)NULL);
  8618. }
  8619. #else   /* atarist */
  8620. /* Return the full pathname for FILENAME by searching along PATH.
  8621.    When found, return the stat () info for FILENAME in FINFO.
  8622.    If PATH is NULL, only the current directory is searched.
  8623.    If the file could not be found, return a NULL pointer. */
  8624. char *
  8625. get_file_info_in_path (filename, path, finfo)
  8626.      char *filename, *path;
  8627.      struct stat *finfo;
  8628. {
  8629.   char *fullpath;
  8630.   extern char *findfile (char *fname, char *path, char **ext);
  8631.  
  8632.   if ((fullpath = findfile(filename, path, (char **)0)) == (char *)NULL)
  8633.     return fullpath;
  8634.  
  8635.   /* findfile() returns an address of a static buffer */
  8636.  
  8637.   if (stat (fullpath, finfo) == 0)
  8638.     return (savestring (fullpath));
  8639.   else
  8640.     return ((char *)NULL);
  8641. }
  8642. #endif  /* atarist */
  8643.  
  8644. #define TAG_TABLE_BEG_STRING "\nTag Table:\n"
  8645. #define NODE_ID "Node:"
  8646. #define NODEEND_SEQUENCE "\n\037"
  8647. #define SLACK 8
  8648.  
  8649. /* Build a new tag_table from a freshly loaded info file.
  8650.    We need that for splitting, so the only info filled out
  8651.    is next_ent, node name and position.
  8652.    Takes buffer with a freshly loaded info file and its size.
  8653.    Returns a pointer to a newly created tag_table or NULL in
  8654.    a case of failure. */
  8655.  
  8656. TAG_ENTRY *
  8657. restore_tag_table (the_file, size)
  8658.      char *the_file;
  8659.      size_t size;
  8660. {
  8661.    size_t loc = size;
  8662.    char *node;
  8663.  
  8664.    loc -= sizeof(TAG_TABLE_END_STRING) + SLACK;  /* leave some reserve
  8665.                             for a trailing garbage */
  8666.    if ((-1L) == (loc = search_forward (TAG_TABLE_END_STRING, loc)))
  8667.      return NULL;                  /* no tag table */
  8668.    loc -= sizeof(NODEEND_SEQUENCE) + sizeof(NODE_ID);
  8669.    while (loc > 0)
  8670.      {
  8671.        if (0 == strncmp (NODEEND_SEQUENCE, &the_file[loc],
  8672.              sizeof(NODEEND_SEQUENCE) - 1))
  8673.      break;
  8674.        loc -= 1;
  8675.      }
  8676.    if (0 == loc)
  8677.      return NULL;
  8678.    loc += sizeof(NODEEND_SEQUENCE) - 1;
  8679.    if ((-1L) == (loc = search_forward (TAG_TABLE_BEG_STRING, loc)))
  8680.      return NULL;    /* we are lost - get out */
  8681.  
  8682.    /* if we are here the we found at last where out tags start */
  8683.    loc += sizeof(TAG_TABLE_BEG_STRING) - 1;
  8684.  
  8685.    init_tag_table ();   /* this actually frees entries in tag_table */
  8686.    while ((-1L) != (loc = search_forward (NODE_ID, loc)))
  8687.      {
  8688.        loc += sizeof(NODE_ID);  /* one extra character for a trailing space */
  8689.        node = &the_file[loc];
  8690.        while (loc < size_of_input_text && '\177' != the_file[loc])
  8691.      loc += 1;
  8692.        if (loc == size_of_input_text)
  8693.      {
  8694.        /* we are way too far */
  8695.        init_tag_table ();
  8696.        return NULL;
  8697.      }
  8698.        the_file[loc++] = '\0'; /* terminate node name */
  8699.        /* add new TAG_ENTRY to the the list */
  8700.        {
  8701.      char *endp;  /* for strtol() */
  8702.      TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
  8703. #if !defined (HAVE_BZERO)
  8704.      memset (new, 0, sizeof (TAG_ENTRY));
  8705. #else
  8706.      bzero (new, sizeof (TAG_ENTRY));
  8707. #endif /* !BZERO_MISSING */
  8708.      new->node = savestring(node);
  8709.      new->next_ent = tag_table;
  8710.      tag_table = new;
  8711.      /* this should be strtoul but it is broken in a quite
  8712.         few systems - sigh... */
  8713.      new->position = strtol (&the_file[loc], &endp, 10);
  8714.      loc = endp - the_file;
  8715.        }
  8716.      }
  8717.    tag_table = (TAG_ENTRY *) reverse_list ((GENERIC_LIST *)tag_table);
  8718.    return tag_table;
  8719. }
  8720.